DEADSOFTWARE

HolmesUI: rendering cosmetix
[d2df-sdl.git] / src / gx / gh_flexlay.pas
index cc01bb69fa685eb0750cfe1bffcf289513763a55..9692f0d1353f0319fb407204f7feb37dcf8338de 100644 (file)
@@ -5,13 +5,13 @@ unit gh_flexlay;
 first pass:
   set all 'temp-flex' flags for controls to 'flex'
   reset all 'laywrap' flags for controls
-  build group arrays; for each group: find max size for group, adjust 'wantsize' controls to group max size
+  build group arrays; for each group: find max size for group, adjust 'startsize' controls to group max size
   call 'calc max size' for top-level control
   flags set:
     'firsttime'
 
 second pass:
-  calcluate desired sizes (process flexes) using 'wantsize', set 'desiredsize' and 'desiredpos'
+  calcluate desired sizes (process flexes) using 'startsize', set 'desiredsize' and 'desiredpos'
     if control has children, call 'second pass' recursively with this control as parent
   flags set:
     'group-element-changed', if any group element size was changed
@@ -19,8 +19,8 @@ second pass:
 
 third pass:
   if 'group-element-changed':
-    for each group: adjust controls to max desired size (wantsize), set 'temp-flex' flags to 0 for 'em, set 'second-again' flag
-  for other controls: if 'desiredsize' > 'maxsize', set 'wantsize' to 'maxsize', set 'temp-flex' flag to 0, set 'second-again' flag
+    for each group: adjust controls to max desired size (startsize), set 'temp-flex' flags to 0 for 'em, set 'second-again' flag
+  for other controls: if 'desiredsize' > 'maxsize', set 'startsize' to 'maxsize', set 'temp-flex' flag to 0, set 'second-again' flag
   if 'second-again' or 'wrapping-changed':
     reset 'second-again'
     reset 'wrapping-changed'
@@ -32,17 +32,17 @@ fourth pass:
   return
 
 calc max size:
-  set 'wantsize' to max(size, maxsize, 0)
+  set 'startsize' to max(size, maxsize, 0)
   if 'size' is negative:
     set 'temp-flex' flag to 0
   if has children:
     call 'calc max size' for each child
-    set 'desiredmax' to 'wantsize'
+    set 'desiredmax' to 'startsize'
     do lines, don't distribute space (i.e. calc only wrapping),
       for each complete line, set 'desiredmax' to max(desiredmax, linesize)
     if 'maxsize' >= 0:
       set 'desiredmax' to min(desiredmax, maxsize)
-    set 'wantsize' to 'desiredmax'
+    set 'startsize' to 'desiredmax'
   return
 
 
@@ -62,8 +62,12 @@ for wrapping:
 
 
 (*
+  control default size will be increased by margins
+  negative margins are ignored
 ControlT:
+  procedure layPrepare (); // called before registering control in layouter
   function getDefSize (): TLaySize; // default size; <0: use max size
+  function getMargins (): TLayMargins;
   function getMaxSize (): TLaySize; // max size; <0: set to some huge value
   function getFlex (): Integer; // <=0: not flexible
   function isHorizBox (): Boolean; // horizontal layout for children?
@@ -114,7 +118,10 @@ type
         tempFlex: Integer;
         flags: LongWord; // see below
         aligndir: Integer;
-        wantsize, desiredsize, maxsize: TLaySize;
+        startsize: TLaySize; // current
+        desiredsize: TLaySize;
+        maxsize: TLaySize;
+        margins: TLayMargins; // can never be negative
         desiredpos: TLayPos;
         ctl: ControlT;
         parent: LayControlIdx; // = -1;
@@ -132,11 +139,20 @@ type
         function lineStart (): Boolean; inline;
         function canWrap (): Boolean; inline;
         function inGroup (): Boolean; inline;
-        function expand (): Boolean; inline;
         function firstInLine (): Boolean; inline;
 
+        function getExpand (): Boolean; inline;
+        procedure setExpand (v: Boolean); inline;
+
+        function alignLeft (): Boolean; inline;
+        function alignTop (): Boolean; inline;
+        function alignRight (): Boolean; inline;
+        function alignBottom (): Boolean; inline;
+        function alignCenter (): Boolean; inline;
+
       public
         property didWrap: Boolean read getDidWrap write setDidWrap;
+        property expand: Boolean read getExpand write setExpand;
       end;
 
       PLayGroup = ^TLayGroup;
@@ -165,7 +181,7 @@ type
     // this also sets `tempFlex`
     procedure calcMaxSizeInternal (cidx: LayControlIdx);
 
-    procedure fixLine (me: PLayControl; i0, i1: LayControlIdx; cury: Integer; var spaceLeft: Single; var flexTotal: Integer; var flexBoxCount: Integer);
+    procedure fixLine (me: PLayControl; i0, i1: LayControlIdx; var cury: Integer; var spaceLeft: Single);
     // do box layouting; call `layBox()` recursively if necessary
     procedure layBox (boxidx: LayControlIdx);
 
@@ -229,12 +245,20 @@ function TFlexLayouterBase.TLayControl.horizBox (): Boolean; inline; begin resul
 function TFlexLayouterBase.TLayControl.lineStart (): Boolean; inline; begin result := ((flags and FlagLineStart) <> 0); end;
 function TFlexLayouterBase.TLayControl.canWrap (): Boolean; inline; begin result := ((flags and FlagLineCanWrap) <> 0); end;
 function TFlexLayouterBase.TLayControl.inGroup (): Boolean; inline; begin result := ((flags and FlagInGroup) <> 0); end;
-function TFlexLayouterBase.TLayControl.expand (): Boolean; inline; begin result := ((flags and FlagExpand) <> 0); end;
 function TFlexLayouterBase.TLayControl.firstInLine (): Boolean; inline; begin result := ((flags and FlagLineFirst) <> 0); end;
 
 function TFlexLayouterBase.TLayControl.getDidWrap (): Boolean; inline; begin result := ((flags and FlagLineDidWrap) <> 0); end;
 procedure TFlexLayouterBase.TLayControl.setDidWrap (v: Boolean); inline; begin if (v) then flags := flags or FlagLineDidWrap else flags := flags and (not FlagLineDidWrap); end;
 
+function TFlexLayouterBase.TLayControl.getExpand (): Boolean; inline; begin result := ((flags and FlagExpand) <> 0); end;
+procedure TFlexLayouterBase.TLayControl.setExpand (v: Boolean); inline; begin if (v) then flags := flags or FlagExpand else flags := flags and (not FlagExpand); end;
+
+function TFlexLayouterBase.TLayControl.alignLeft (): Boolean; inline; begin result := (aligndir < 0); end;
+function TFlexLayouterBase.TLayControl.alignTop (): Boolean; inline; begin result := (aligndir < 0); end;
+function TFlexLayouterBase.TLayControl.alignRight (): Boolean; inline; begin result := (aligndir > 0); end;
+function TFlexLayouterBase.TLayControl.alignBottom (): Boolean; inline; begin result := (aligndir > 0); end;
+function TFlexLayouterBase.TLayControl.alignCenter (): Boolean; inline; begin result := (aligndir = 0); end;
+
 
 // ////////////////////////////////////////////////////////////////////////// //
 constructor TFlexLayouterBase.TChildrenEnumerator.Create (constref actls: TLayCtlArray; acur: Integer);
@@ -327,8 +351,10 @@ begin
   assert(ctlist[parent].firstChild = -1);
   while (child <> nil) do
   begin
+    child.layPrepare;
     SetLength(ctlist, Length(ctlist)+1);
     lc := @ctlist[High(ctlist)];
+    lc.initialize();
     if (cidx = -1) then
     begin
       cidx := LayControlIdx(High(ctlist));
@@ -396,8 +422,10 @@ procedure TFlexLayouterBase.setup (root: ControlT);
 begin
   clear();
   if (root = nil) then exit;
+  root.layPrepare;
   try
     SetLength(ctlist, 1);
+    ctlist[0].initialize();
     ctlist[0].myidx := 0;
     ctlist[0].ctl := root;
     fixFlags(0);
@@ -416,7 +444,8 @@ procedure TFlexLayouterBase.calcMaxSizeInternal (cidx: LayControlIdx);
 var
   lc, c: PLayControl;
   msz: TLaySize;
-  negw{, negh}: Boolean;
+  negw, negh: Boolean;
+  zerow: Boolean;
   curwdt, curhgt, totalhgt: Integer;
   doWrap: Boolean;
 begin
@@ -424,14 +453,9 @@ begin
 
   lc := @ctlist[cidx];
   msz := lc.ctl.getMaxSize;
-  //lc.wantsize := lc.ctl.getDefSize;
-  negw := (lc.wantsize.w <= 0);
-  //negh := (lc.wantsize.h <= 0);
-
-  //if (lc.wantsize.w < msz.w) lc.wantsize.w := msz.w;
-  //if (lc.wantsize.h < msz.h) lc.wantsize.h := msz.h;
-
-  //writeln('calcsize #', cidx, '; wantsize=', lc.wantsize, '; ctl.maxsize=', msz);
+  negw := (lc.startsize.w < 0);
+  negh := (lc.startsize.h < 0);
+  zerow := (lc.startsize.w = 0);
 
   lc.tempFlex := lc.ctl.getFlex;
 
@@ -441,45 +465,48 @@ begin
   begin
     // horizontal boxes
     if (negw) then lc.tempFlex := 0; // size is negative: don't expand
-    curwdt := 0;
+    curwdt := lc.margins.horiz;
     curhgt := 0;
-    totalhgt := 0;
+    totalhgt := lc.margins.vert;
     for c in forChildren(cidx) do
     begin
       // new line?
       doWrap := (not c.firstInLine) and (c.lineStart);
       // need to wrap?
-      if (not doWrap) and (lc.canWrap) and (c.canWrap) and (msz.w > 0) and (curwdt+c.wantsize.w > lc.wantsize.w) then doWrap := true;
+      if (not doWrap) and zerow and (lc.canWrap) and (c.canWrap) and (msz.w > 0) and (curwdt+c.startsize.w > lc.startsize.w) then doWrap := true;
       if (doWrap) then
       begin
         totalhgt += curhgt;
-        if (lc.wantsize.w < curwdt) then lc.wantsize.w := curwdt;
+        if (lc.startsize.w < curwdt) then lc.startsize.w := curwdt;
         curwdt := 0;
         curhgt := 0;
       end;
-      curwdt += c.wantsize.w;
-      if (curhgt < c.wantsize.h) then curhgt := c.wantsize.h;
+      curwdt += c.startsize.w;
+      if (curhgt < c.startsize.h) then curhgt := c.startsize.h;
     end;
+    //writeln('00: ', cidx, ': totalhgt=', totalhgt);
     totalhgt += curhgt;
-    if (lc.wantsize.w < curwdt) then lc.wantsize.w := curwdt;
-    if (lc.wantsize.h < totalhgt) then lc.wantsize.h := totalhgt;
+    //writeln('01: ', cidx, ': totalhgt=', totalhgt);
+    if (lc.startsize.w < curwdt) then lc.startsize.w := curwdt;
+    if (lc.startsize.h < totalhgt) then lc.startsize.h := totalhgt;
   end
   else
   begin
     // vertical boxes
-    curhgt := 0;
+    if (negh) then lc.tempFlex := 0; // size is negative: don't expand
+    curhgt := lc.margins.vert;
     for c in forChildren(cidx) do
     begin
-      if (lc.wantsize.w < c.wantsize.w) then lc.wantsize.w := c.wantsize.w;
-      curhgt += c.wantsize.h;
+      if (lc.startsize.w < c.startsize.w+lc.margins.horiz) then lc.startsize.w := c.startsize.w+lc.margins.horiz;
+      curhgt += c.startsize.h;
     end;
-    if (lc.wantsize.h < curhgt) then lc.wantsize.h := curhgt;
+    if (lc.startsize.h < curhgt) then lc.startsize.h := curhgt;
   end;
-  if (lc.wantsize.w < 1) then lc.wantsize.w := 1;
-  if (lc.wantsize.h < 1) then lc.wantsize.h := 1;
+  if (lc.startsize.w < 0) then lc.startsize.w := 0;
+  if (lc.startsize.h < 0) then lc.startsize.h := 0;
   lc.maxsize := msz;
-  if (lc.maxsize.w < lc.wantsize.w) then lc.maxsize.w := lc.wantsize.w;
-  if (lc.maxsize.h < lc.wantsize.h) then lc.maxsize.h := lc.wantsize.h;
+  if (lc.maxsize.w < lc.startsize.w) then lc.maxsize.w := lc.startsize.w;
+  if (lc.maxsize.h < lc.startsize.h) then lc.maxsize.h := lc.startsize.h;
 end;
 
 
@@ -491,16 +518,23 @@ var
   grp: PLayGroup;
   maxsz: Integer;
   cidx: LayControlIdx;
+  ct: PLayControl;
+  mr: TLayMargins;
 begin
-  // reset all 'laywrap' flags for controls, set initial 'wantsize'
+  // reset all 'laywrap' flags for controls, set initial 'startsize'
   for f := 0 to High(ctlist) do
   begin
     ctlist[f].didWrap := false;
-    ctlist[f].wantsize := ctlist[f].ctl.getDefSize;
+    ctlist[f].startsize := ctlist[f].ctl.getDefSize;
+    mr := ctlist[f].ctl.getMargins;
+    ctlist[f].margins := mr;
+    //ctlist[f].startsize.w += mr.horiz;
+    //ctlist[f].startsize.h += mr.vert;
   end;
   // setup sizes
   calcMaxSizeInternal(0); // this also sets `tempFlex`
-  // find max size for group, adjust 'wantsize' controls to group max size
+  //writeln('=== calculated max size (0) ==='); dump();
+  // find max size for group, adjust 'startsize' controls to group max size
   needRecalcMaxSize := false;
   for gtype := 0 to 1 do
   begin
@@ -511,39 +545,60 @@ begin
       for c := 0 to High(grp.ctls) do
       begin
         cidx := grp.ctls[c];
-        if (maxsz < ctlist[cidx].wantsize[gtype]) then maxsz := ctlist[cidx].wantsize[gtype];
+        ct := @ctlist[cidx];
+        if (maxsz < ct.startsize[gtype]) then maxsz := ct.startsize[gtype];
       end;
       for c := 0 to High(grp.ctls) do
       begin
         cidx := grp.ctls[c];
-        if (maxsz <> ctlist[cidx].wantsize[gtype]) then
+        ct := @ctlist[cidx];
+        if (maxsz <> ct.startsize[gtype]) then
         begin
           needRecalcMaxSize := true;
-          ctlist[cidx].wantsize[gtype] := maxsz;
+          ct.startsize[gtype] := maxsz;
         end;
       end;
     end;
   end;
   // recalc maxsize if necessary
   if (needRecalcMaxSize) then calcMaxSizeInternal(0);
+  // set "desired size" to "start size"
+  for f := 0 to High(ctlist) do ctlist[f].desiredsize := ctlist[f].startsize;
   // set flags
   firstTime := true;
+  //writeln('=== calculated max size (final) ==='); dump();
 end;
 
 
-procedure TFlexLayouterBase.fixLine (me: PLayControl; i0, i1: LayControlIdx; cury: Integer; var spaceLeft: Single; var flexTotal: Integer; var flexBoxCount: Integer);
+procedure TFlexLayouterBase.fixLine (me: PLayControl; i0, i1: LayControlIdx; var cury: Integer; var spaceLeft: Single);
 var
+  flexTotal: Integer = 0; // total sum of flex fields
+  flexBoxCount: Integer = 0; // number of boxes
   curx: Integer;
   lc: PLayControl;
   osz: TLaySize;
   toadd: Integer;
+  sti0: Integer;
+  lineh: Integer;
 begin
-  curx := 0;
+  curx := me.margins.left;
+  sti0 := i0;
+  // calc minimal line height, count flexboxes
+  lineh := 0;
+  while (i0 <> i1) do
+  begin
+    lc := @ctlist[i0];
+    lineh := nmax(lineh, lc.startsize.h);
+    if (lc.tempFlex > 0) then begin flexTotal += lc.tempFlex; flexBoxCount += 1; end;
+    i0 := lc.nextSibling;
+  end;
+  // distribute space, expand/align
+  i0 := sti0;
   while (i0 <> i1) do
   begin
     lc := @ctlist[i0];
     osz := lc.desiredsize;
-    lc.desiredsize := lc.wantsize;
+    lc.desiredsize := lc.startsize;
     lc.desiredpos.x := curx;
     lc.desiredpos.y := cury;
     curx += lc.desiredsize.w;
@@ -557,17 +612,27 @@ begin
         lc.desiredsize.w += toadd;
         curx += toadd;
         // compensate (crudely) rounding errors
-        if (curx > me.desiredsize.w) then begin lc.desiredsize.w -= 1; curx -= 1; end;
+        if (curx > me.desiredsize.w-me.margins.horiz) then begin lc.desiredsize.w -= 1; curx -= 1; end;
         // relayout children
         layBox(lc.firstChild);
       end;
     end;
-    if (lc.inGroup) and (not lc.desiredsize.equals(osz)) then groupElementChanged := true;
+    // expand or align
+         if (lc.expand) then lc.desiredsize.h := nmin(lc.maxsize.h, lineh) // expand
+    else if (lc.alignBottom) then lc.desiredpos.y := cury+(lineh-lc.desiredsize.h) // bottom align
+    else if (lc.alignCenter) then lc.desiredpos.y := cury+(lineh-lc.desiredsize.h) div 2; // center
+    if (not osz.equals(lc.desiredsize)) then
+    begin
+      if (lc.inGroup) then groupElementChanged := true;
+      // relayout children
+      layBox(lc.firstChild);
+    end;
     i0 := lc.nextSibling;
   end;
   flexTotal := 0;
   flexBoxCount := 0;
-  spaceLeft := me.wantsize.w;
+  spaceLeft := me.desiredsize.w-me.margins.horiz;
+  cury += lineh;
 end;
 
 
@@ -579,33 +644,31 @@ var
   flexBoxCount: Integer; // number of boxes
   spaceLeft: Single;
   cury: Integer;
-  maxwdt, maxhgt: Integer;
+  maxwdt: Integer;
   lineStartIdx: LayControlIdx;
   lc: PLayControl;
   doWrap: Boolean;
   toadd: Integer;
+  osz: TLaySize;
 begin
   if (boxidx < 0) or (boxidx >= Length(ctlist)) then exit;
   me := @ctlist[boxidx];
 
-  // if we have no children, just set desired size and exit
-  me.desiredsize := me.wantsize;
+  // if we have no children, there's nothing to do
   if (me.firstChild = -1) then exit;
 
-  // first, layout all children; also, gather some flex data
+  // first, layout all children
   for lc in forChildren(boxidx) do layBox(lc.myidx);
 
   // second, layout lines, distribute flex data
   if (me.horizBox) then
   begin
     // horizontal boxes
-    cury := 0;
-    maxhgt := 0;
+    cury := me.margins.top;
 
-    fixLine(me, -1, -1, cury, spaceLeft, flexTotal, flexBoxCount); //HACK!
+    fixLine(me, -1, -1, cury, spaceLeft); //HACK!
 
     lineStartIdx := me.firstChild;
-
     for lc in forChildren(boxidx) do
     begin
       // new line?
@@ -615,33 +678,20 @@ begin
       if (doWrap) then
       begin
         // new line, fix this one
-        if (not lc.didWrap) then
-        begin
-          wrappingChanged := true;
-          lc.didWrap := true;
-        end;
-        fixLine(me, lineStartIdx, lc.myidx, cury, spaceLeft, flexTotal, flexBoxCount);
-        cury += maxhgt;
+        if (not lc.didWrap) then begin wrappingChanged := true; lc.didWrap := true; end;
+        fixLine(me, lineStartIdx, lc.myidx, cury, spaceLeft);
         lineStartIdx := lc.myidx;
       end
       else
       begin
-        if (lc.didWrap) then
-        begin
-          wrappingChanged := true;
-          lc.didWrap := false;
-        end;
+        if (lc.didWrap) then begin wrappingChanged := true; lc.didWrap := false; end;
       end;
       spaceLeft -= lc.desiredsize.w;
-      if (maxhgt < lc.desiredsize.h) then maxhgt := lc.desiredsize.h;
-      if (lc.tempFlex > 0) then
-      begin
-        flexTotal += lc.tempFlex;
-        flexBoxCount += 1;
-      end;
+      //if (maxhgt < lc.desiredsize.h) then maxhgt := lc.desiredsize.h;
+      //if (lc.tempFlex > 0) then begin flexTotal += lc.tempFlex; flexBoxCount += 1; end;
     end;
     // fix last line
-    fixLine(me, lineStartIdx, -1, cury, spaceLeft, flexTotal, flexBoxCount);
+    fixLine(me, lineStartIdx, -1, cury, spaceLeft);
   end
   else
   begin
@@ -649,26 +699,24 @@ begin
     maxwdt := 0;
     flexTotal := 0;
     flexBoxCount := 0;
-    spaceLeft := me.wantsize.h;
+    spaceLeft := me.desiredsize.h-me.margins.vert;
 
     // calc flex
     for lc in forChildren(boxidx) do
     begin
       spaceLeft -= lc.desiredsize.h;
       if (maxwdt < lc.desiredsize.w) then maxwdt := lc.desiredsize.w;
-      if (lc.tempFlex > 0) then
-      begin
-        flexTotal += lc.tempFlex;
-        flexBoxCount += 1;
-      end;
+      if (lc.tempFlex > 0) then begin flexTotal += lc.tempFlex; flexBoxCount += 1; end;
     end;
 
     // distribute space
-    cury := 0;
+    cury := me.margins.top;
+    //writeln('me: ', boxidx, '; margins: ', me.margins.toString);
     for lc in forChildren(boxidx) do
     begin
-      lc.desiredsize := lc.wantsize;
-      lc.desiredpos.x := 0;
+      osz := lc.desiredsize;
+      lc.desiredsize := lc.startsize;
+      lc.desiredpos.x := me.margins.left;
       lc.desiredpos.y := cury;
       cury += lc.desiredsize.h;
       // fix flexbox size
@@ -681,11 +729,19 @@ begin
           lc.desiredsize.h += toadd;
           cury += toadd;
           // compensate (crudely) rounding errors
-          if (cury > me.desiredsize.h) then begin lc.desiredsize.h -= 1; cury -= 1; end;
-          // relayout children
-          layBox(lc.firstChild);
+          if (cury > me.desiredsize.h-me.margins.vert) then begin lc.desiredsize.h -= 1; cury -= 1; end;
         end;
       end;
+      // expand or align
+           if (lc.expand) then lc.desiredsize.w := nmin(lc.maxsize.w, me.desiredsize.w-me.margins.vert) // expand
+      else if (lc.alignRight) then lc.desiredpos.x := me.desiredsize.w-me.margins.right-lc.desiredsize.w // right align
+      else if (lc.alignCenter) then lc.desiredpos.x := (me.desiredsize.w-lc.desiredsize.w) div 2; // center
+      if (not osz.equals(lc.desiredsize)) then
+      begin
+        if (lc.inGroup) then groupElementChanged := true;
+        // relayout children
+        layBox(lc.firstChild);
+      end;
     end;
   end;
 end;
@@ -693,7 +749,7 @@ end;
 
 (*
 second pass:
-  calcluate desired sizes (process flexes) using 'wantsize', set 'desiredsize' and 'desiredpos'
+  calcluate desired sizes (process flexes) using 'startsize', set 'desiredsize' and 'desiredpos'
     if control has children, call 'second pass' recursively with this control as parent
   flags set:
     'group-element-changed', if any group element size was changed
@@ -719,8 +775,8 @@ end;
 (*
 third pass:
   if 'group-element-changed':
-    for each group: adjust controls to max desired size (wantsize), set 'temp-flex' flags to 0 for 'em, set 'second-again' flag
-  for other controls: if 'desiredsize' > 'maxsize', set 'wantsize' to 'maxsize', set 'temp-flex' flag to 0, set 'second-again' flag
+    for each group: adjust controls to max desired size (startsize), set 'temp-flex' flags to 0 for 'em, set 'second-again' flag
+  for other controls: if 'desiredsize' > 'maxsize', set 'startsize' to 'maxsize', set 'temp-flex' flag to 0, set 'second-again' flag
   if 'second-again' or 'wrapping-changed':
     reset 'second-again'
     reset 'wrapping-changed'
@@ -730,17 +786,71 @@ third pass:
 procedure TFlexLayouterBase.thirdPass ();
 var
   secondAgain: Boolean;
+  gtype: Integer;
+  maxsz: Integer;
+  grp: PLayGroup;
+  f, c: Integer;
+  cidx: LayControlIdx;
+  ct: PLayControl;
 begin
   while true do
   begin
+    secondPass();
     secondAgain := false;
     if (groupElementChanged) then
     begin
-      // do it
+      secondAgain := true;
+      // find max size for group, adjust 'startsize' controls to group max size
+      for gtype := 0 to 1 do
+      begin
+        for f := 0 to High(groups[gtype]) do
+        begin
+          grp := @groups[gtype][f];
+          maxsz := 0;
+          for c := 0 to High(grp.ctls) do
+          begin
+            cidx := grp.ctls[c];
+            ct := @ctlist[cidx];
+            ct.expand := false; // don't expand grouped controls anymore
+            if (maxsz < ct.startsize[gtype]) then maxsz := ct.startsize[gtype];
+          end;
+          for c := 0 to High(grp.ctls) do
+          begin
+            cidx := grp.ctls[c];
+            ct := @ctlist[cidx];
+            ct.startsize[gtype] := maxsz;
+            ct.desiredsize[gtype] := maxsz;
+            ct.tempFlex := 0; // don't change control size anymore
+          end;
+        end;
+      end;
+    end
+    else
+    begin
+      for f := 0 to High(ctlist) do
+      begin
+        ct := @ctlist[f];
+        if (ct.inGroup) then
+        begin
+          ct.expand := false; // don't expand grouped controls anymore
+          ct.tempFlex := 0; // don't change control size anymore
+        end;
+        for c := 0 to 1 do
+        begin
+          if (ct.maxsize[c] < 0) then continue;
+          if (ct.desiredsize[c] > ct.maxsize[c]) then
+          begin
+            //writeln('ctl #', f, '; dimension #', c, ': desired=', ctlist[f].desiredsize[c], '; max=', ctlist[f].maxsize[c]);
+            ct.startsize[c] := ct.maxsize[c];
+            ct.desiredsize[c] := ct.maxsize[c];
+            ct.tempFlex := 0; // don't change control size anymore
+            secondAgain := true;
+          end;
+        end;
+      end;
     end;
     if (not secondAgain) and (not wrappingChanged) then break;
     firstTime := false;
-    secondPass();
   end;
 end;
 
@@ -781,7 +891,7 @@ begin
     lc := @ctlist[f];
     ds := lc.ctl.getDefSize;
     ms := lc.ctl.getMaxSize;
-    writeln(lc.myidx, ': wantsize:', lc.wantsize.toString(), '; desiredsize=', lc.desiredsize.toString(), '; maxsize=', lc.maxsize.toString(), '; tempFlex=', lc.tempFlex, '; flags=', lc.flags,
+    writeln(lc.myidx, ': startsize:', lc.startsize.toString(), '; desiredsize=', lc.desiredsize.toString(), '; maxsize=', lc.maxsize.toString(), '; tempFlex=', lc.tempFlex, '; flags=', lc.flags,
       '; parent=', lc.parent, '; next=', lc.nextSibling, '; child=', lc.firstChild, '; ctl.size=', ds.toString(), '; ctl.maxsize=', ms.toString());
   end;
 end;
@@ -795,8 +905,8 @@ begin
   while (cidx >= 0) do
   begin
     lc := @ctlist[cidx];
-    for f := 0 to High(indent) do write(' ');
-    writeln(lc.myidx, ': wantsize:', lc.wantsize.toString, '; desiredsize=', lc.desiredsize.toString, '; maxsize=', lc.maxsize.toString, '; tempFlex=', lc.tempFlex, '; despos=', lc.desiredpos.toString);
+    for f := 0 to indent do write(' ');
+    writeln(lc.myidx, ': startsize:', lc.startsize.toString, '; desiredsize=', lc.desiredsize.toString, '; maxsize=', lc.maxsize.toString, '; tempFlex=', lc.tempFlex, '; despos=', lc.desiredpos.toString);
     dumpList(lc.firstChild, indent+2);
     cidx := lc.nextSibling;
   end;