1 {$INCLUDE ../shared/a_modes.inc}
6 set all 'temp-flex' flags for controls to 'flex'
7 reset all 'laywrap' flags for controls
8 build group arrays; for each group: find max size for group, adjust 'startsize' controls to group max size
9 call 'calc max size' for top-level control
14 calcluate desired sizes (process flexes) using 'startsize', set 'desiredsize' and 'desiredpos'
15 if control has children, call 'second pass' recursively with this control as parent
17 'group-element-changed', if any group element size was changed
18 'wrapping-changed', if not 'firsttime', and wrapping was changed (i.e. first pass will not set the flag)
21 if 'group-element-changed':
22 for each group: adjust controls to max desired size (startsize), set 'temp-flex' flags to 0 for 'em, set 'second-again' flag
23 for other controls: if 'desiredsize' > 'maxsize', set 'startsize' to 'maxsize', set 'temp-flex' flag to 0, set 'second-again' flag
24 if 'second-again' or 'wrapping-changed':
26 reset 'wrapping-changed'
31 set 'actualsize' and 'actualpos' to 'desiredsize' and 'desiredpos'
35 set 'startsize' to max(size, maxsize, 0)
36 if 'size' is negative:
37 set 'temp-flex' flag to 0
39 call 'calc max size' for each child
40 set 'desiredmax' to 'startsize'
41 do lines, don't distribute space (i.e. calc only wrapping),
42 for each complete line, set 'desiredmax' to max(desiredmax, linesize)
44 set 'desiredmax' to min(desiredmax, maxsize)
45 set 'startsize' to 'desiredmax'
50 try to stuff controls in line until line width is less or equal to maxsize
51 distribute flex for filled line
52 continue until we still has something to stuff
56 we'll hold 'laywrap' flag for each control; it will be set if this control
57 starts a new line (even if this is the first control in line, as it is obviously
60 on redoing second pass, if 'laywrap' flag changed, set 'wrapping-changed' flag
65 control default size will be increased by margins
66 negative margins are ignored
68 procedure layPrepare (); // called before registering control in layouter
69 function getDefSize (): TLaySize; // default size; <0: use max size
70 function getMargins (): TLayMargins;
71 function getMaxSize (): TLaySize; // max size; <0: set to some huge value
72 function getFlex (): Integer; // <=0: not flexible
73 function isHorizBox (): Boolean; // horizontal layout for children?
74 function canWrap (): Boolean; // for horizontal boxes: can wrap children? for child: `false` means 'nonbreakable at *next* ctl'
75 function isLineStart (): Boolean; // `true` if this ctl should start a new line; ignored for vertical boxes
76 function getAlign (): Integer; // aligning in non-main direction: <0: left/up; 0: center; >0: right/down
77 function getExpand (): Boolean; // expanding in non-main direction: `true` will ignore align and eat all available space
78 procedure setActualSizePos (constref apos: TLayPos; constref asize: TLaySize);
79 function getHGroup (): AnsiString; // empty: not grouped
80 function getVGroup (): AnsiString; // empty: not grouped
81 function nextSibling (): ControlT;
82 function firstChild (): ControlT;
91 // ////////////////////////////////////////////////////////////////////////// //
93 generic TFlexLayouterBase
<ControlT
> = class
98 type LayControlIdx
= Integer;
103 FlagHorizBox
= LongWord(1) shl 0; // horizontal layout for children
104 FlagLineStart
= LongWord(1) shl 1;
105 FlagLineCanWrap
= LongWord(1) shl 2;
107 FlagLineDidWrap
= LongWord(1) shl 3; // will be set when line was wrapped
108 FlagInGroup
= LongWord(1) shl 4; // set if this control is a member of any group
109 FlagExpand
= LongWord(1) shl 5;
110 FlagLineFirst
= LongWord(1) shl 6;
114 PLayControl
= ^TLayControl
;
117 myidx
: LayControlIdx
;
119 flags
: LongWord; // see below
121 startsize
: TLaySize
; // current
122 desiredsize
: TLaySize
;
124 margins
: TLayMargins
; // can never be negative
127 parent
: LayControlIdx
; // = -1;
128 firstChild
: LayControlIdx
; // = -1;
129 nextSibling
: LayControlIdx
; // = -1;
132 function getDidWrap (): Boolean; inline;
133 procedure setDidWrap (v
: Boolean); inline;
136 procedure initialize (); inline;
138 function horizBox (): Boolean; inline;
139 function lineStart (): Boolean; inline;
140 function canWrap (): Boolean; inline;
141 function inGroup (): Boolean; inline;
142 function firstInLine (): Boolean; inline;
144 function getExpand (): Boolean; inline;
145 procedure setExpand (v
: Boolean); inline;
148 property didWrap
: Boolean read getDidWrap write setDidWrap
;
149 property expand
: Boolean read getExpand write setExpand
;
152 PLayGroup
= ^TLayGroup
;
155 ctls
: array of LayControlIdx
;
158 TLayCtlArray
= array of TLayControl
;
159 TLayGrpArray
= array of TLayGroup
;
162 ctlist
: TLayCtlArray
;
163 groups
: array[0..1] of TLayGrpArray
; // horiz, vert
166 groupElementChanged
: Boolean;
167 wrappingChanged
: Boolean;
170 procedure fixFlags (cidx
: LayControlIdx
);
171 procedure doChildren (parent
: LayControlIdx
; child
: ControlT
);
172 procedure appendToGroup (const gname
: AnsiString;cidx
: LayControlIdx
;gidx
: Integer);
173 procedure setupGroups ();
175 // this also sets `tempFlex`
176 procedure calcMaxSizeInternal (cidx
: LayControlIdx
);
178 procedure fixLine (me
: PLayControl
; i0
, i1
: LayControlIdx
; cury
: Integer; var spaceLeft
: Single; var flexTotal
: Integer; var flexBoxCount
: Integer);
179 // do box layouting; call `layBox()` recursively if necessary
180 procedure layBox (boxidx
: LayControlIdx
);
182 procedure firstPass ();
183 procedure secondPass ();
184 procedure thirdPass ();
185 procedure fourthPass ();
187 procedure dumpList (cidx
: LayControlIdx
; indent
: Integer);
191 TChildrenEnumerator
= record
197 constructor Create (constref actls
: TLayCtlArray
; acur
: Integer);
198 function moveNext (): Boolean; inline;
199 function getCurrent (): PLayControl
; inline;
200 function getEnumerator (): TChildrenEnumerator
; inline;
201 property current
: PLayControl read getCurrent
;
205 constructor Create ();
206 destructor Destroy (); override;
211 // build control and group lists
212 procedure setup (root
: ControlT
);
214 function forChildren (cidx
: LayControlIdx
): TChildrenEnumerator
; inline;
218 procedure dumpFlat ();
229 // ////////////////////////////////////////////////////////////////////////// //
230 procedure TFlexLayouterBase
.TLayControl
.initialize (); inline;
232 FillChar(self
, 0, sizeof(self
));
238 function TFlexLayouterBase
.TLayControl
.horizBox (): Boolean; inline; begin result
:= ((flags
and FlagHorizBox
) <> 0); end;
239 function TFlexLayouterBase
.TLayControl
.lineStart (): Boolean; inline; begin result
:= ((flags
and FlagLineStart
) <> 0); end;
240 function TFlexLayouterBase
.TLayControl
.canWrap (): Boolean; inline; begin result
:= ((flags
and FlagLineCanWrap
) <> 0); end;
241 function TFlexLayouterBase
.TLayControl
.inGroup (): Boolean; inline; begin result
:= ((flags
and FlagInGroup
) <> 0); end;
242 function TFlexLayouterBase
.TLayControl
.firstInLine (): Boolean; inline; begin result
:= ((flags
and FlagLineFirst
) <> 0); end;
244 function TFlexLayouterBase
.TLayControl
.getDidWrap (): Boolean; inline; begin result
:= ((flags
and FlagLineDidWrap
) <> 0); end;
245 procedure TFlexLayouterBase
.TLayControl
.setDidWrap (v
: Boolean); inline; begin if (v
) then flags
:= flags
or FlagLineDidWrap
else flags
:= flags
and (not FlagLineDidWrap
); end;
247 function TFlexLayouterBase
.TLayControl
.getExpand (): Boolean; inline; begin result
:= ((flags
and FlagExpand
) <> 0); end;
248 procedure TFlexLayouterBase
.TLayControl
.setExpand (v
: Boolean); inline; begin if (v
) then flags
:= flags
or FlagExpand
else flags
:= flags
and (not FlagExpand
); end;
251 // ////////////////////////////////////////////////////////////////////////// //
252 constructor TFlexLayouterBase
.TChildrenEnumerator
.Create (constref actls
: TLayCtlArray
; acur
: Integer);
259 function TFlexLayouterBase
.TChildrenEnumerator
.moveNext (): Boolean; inline;
263 if (cur
>= 0) and (cur
< Length(ctls
)) then cur
:= ctls
[cur
].firstChild
else cur
:= -1;
268 cur
:= ctls
[cur
].nextSibling
;
270 result
:= (cur
>= 0);
273 function TFlexLayouterBase
.TChildrenEnumerator
.getCurrent (): PLayControl
; inline;
275 result
:= @ctls
[cur
];
278 function TFlexLayouterBase
.TChildrenEnumerator
.getEnumerator (): TChildrenEnumerator
; inline;
284 // ////////////////////////////////////////////////////////////////////////// //
285 constructor TFlexLayouterBase
.Create ();
292 groupElementChanged
:= false;
293 wrappingChanged
:= false;
297 destructor TFlexLayouterBase
.Destroy ();
304 function TFlexLayouterBase
.forChildren (cidx
: LayControlIdx
): TChildrenEnumerator
; inline;
306 result
:= TChildrenEnumerator
.Create(ctlist
, cidx
);
310 procedure TFlexLayouterBase
.clear ();
318 procedure TFlexLayouterBase
.fixFlags (cidx
: LayControlIdx
);
322 assert((cidx
>= 0) and (cidx
< Length(ctlist
)));
325 if (lc
.ctl
.isHorizBox
) then lc
.flags
:= lc
.flags
or FlagHorizBox
;
326 if (lc
.ctl
.isLineStart
) then lc
.flags
:= lc
.flags
or FlagLineStart
;
327 if (lc
.ctl
.canWrap
) then lc
.flags
:= lc
.flags
or FlagLineCanWrap
;
328 if (lc
.ctl
.getExpand
) then lc
.flags
:= lc
.flags
or FlagExpand
;
329 lc
.aligndir
:= lc
.ctl
.getAlign
;
333 procedure TFlexLayouterBase
.doChildren (parent
: LayControlIdx
; child
: ControlT
);
335 cidx
: LayControlIdx
= -1;
338 assert((parent
>= 0) and (parent
< Length(ctlist
)));
339 assert(ctlist
[parent
].firstChild
= -1);
340 while (child
<> nil) do
343 SetLength(ctlist
, Length(ctlist
)+1);
344 lc
:= @ctlist
[High(ctlist
)];
348 cidx
:= LayControlIdx(High(ctlist
));
349 ctlist
[parent
].firstChild
:= cidx
;
350 // first child is always linestart
351 lc
.flags
:= lc
.flags
or FlagLineStart
or FlagLineFirst
;
355 ctlist
[cidx
].nextSibling
:= LayControlIdx(High(ctlist
));
356 cidx
:= LayControlIdx(High(ctlist
));
362 doChildren(cidx
, child
.firstChild
);
363 child
:= child
.nextSibling
;
368 procedure TFlexLayouterBase
.appendToGroup (const gname
: AnsiString; cidx
: LayControlIdx
; gidx
: Integer);
372 if (Length(gname
) = 0) then exit
;
373 assert((cidx
>= 0) and (cidx
< Length(ctlist
)));
374 assert((gidx
= 0) or (gidx
= 1));
375 ctlist
[cidx
].flags
:= ctlist
[cidx
].flags
or FlagInGroup
;
376 for f
:= 0 to High(groups
[gidx
]) do
378 if (groups
[gidx
][f
].name
= gname
) then
380 SetLength(groups
[gidx
][f
].ctls
, Length(groups
[gidx
][f
].ctls
)+1);
381 groups
[gidx
][f
].ctls
[High(groups
[gidx
][f
].ctls
)] := cidx
;
386 f
:= Length(groups
[gidx
]);
387 SetLength(groups
[gidx
], f
+1);
388 groups
[gidx
][f
].name
:= gname
;
389 SetLength(groups
[gidx
][f
].ctls
, Length(groups
[gidx
][f
].ctls
)+1);
390 groups
[gidx
][f
].ctls
[High(groups
[gidx
][f
].ctls
)] := cidx
;
394 procedure TFlexLayouterBase
.setupGroups ();
399 for idx
:= 0 to High(ctlist
) do
402 appendToGroup(lc
.ctl
.getHGroup
, LayControlIdx(idx
), 0);
403 appendToGroup(lc
.ctl
.getVGroup
, LayControlIdx(idx
), 1);
408 // build control and group lists
409 procedure TFlexLayouterBase
.setup (root
: ControlT
);
412 if (root
= nil) then exit
;
415 SetLength(ctlist
, 1);
416 ctlist
[0].initialize();
417 ctlist
[0].myidx
:= 0;
418 ctlist
[0].ctl
:= root
;
420 ctlist
[0].flags
:= ctlist
[0].flags
or FlagLineStart
or FlagLineFirst
;
421 doChildren(0, root
.firstChild
);
430 // this also sets `tempFlex`
431 procedure TFlexLayouterBase
.calcMaxSizeInternal (cidx
: LayControlIdx
);
436 curwdt
, curhgt
, totalhgt
: Integer;
439 if (cidx
< 0) or (cidx
>= Length(ctlist
)) then exit
;
442 msz
:= lc
.ctl
.getMaxSize
;
443 negw
:= (lc
.startsize
.w
<= 0);
444 negh
:= (lc
.startsize
.h
<= 0);
446 lc
.tempFlex
:= lc
.ctl
.getFlex
;
448 for c
in forChildren(cidx
) do calcMaxSizeInternal(c
.myidx
);
450 if (lc
.horizBox
) then
453 if (negw
) then lc
.tempFlex
:= 0; // size is negative: don't expand
454 curwdt
:= lc
.margins
.horiz
;
455 curhgt
:= lc
.margins
.vert
;
457 for c
in forChildren(cidx
) do
460 doWrap
:= (not c
.firstInLine
) and (c
.lineStart
);
462 if (not doWrap
) and (lc
.canWrap
) and (c
.canWrap
) and (msz
.w
> 0) and (curwdt
+c
.startsize
.w
> lc
.startsize
.w
) then doWrap
:= true;
466 if (lc
.startsize
.w
< curwdt
) then lc
.startsize
.w
:= curwdt
;
470 curwdt
+= c
.startsize
.w
;
471 if (curhgt
< c
.startsize
.h
) then curhgt
:= c
.startsize
.h
;
474 if (lc
.startsize
.w
< curwdt
) then lc
.startsize
.w
:= curwdt
;
475 if (lc
.startsize
.h
< totalhgt
) then lc
.startsize
.h
:= totalhgt
;
480 if (negh
) then lc
.tempFlex
:= 0; // size is negative: don't expand
481 curhgt
:= lc
.margins
.vert
;
482 for c
in forChildren(cidx
) do
484 if (lc
.startsize
.w
< c
.startsize
.w
+lc
.margins
.horiz
) then lc
.startsize
.w
:= c
.startsize
.w
+lc
.margins
.horiz
;
485 curhgt
+= c
.startsize
.h
;
487 if (lc
.startsize
.h
< curhgt
) then lc
.startsize
.h
:= curhgt
;
489 if (lc
.startsize
.w
< 0) then lc
.startsize
.w
:= 0;
490 if (lc
.startsize
.h
< 0) then lc
.startsize
.h
:= 0;
492 if (lc
.maxsize
.w
< lc
.startsize
.w
) then lc
.maxsize
.w
:= lc
.startsize
.w
;
493 if (lc
.maxsize
.h
< lc
.startsize
.h
) then lc
.maxsize
.h
:= lc
.startsize
.h
;
497 procedure TFlexLayouterBase
.firstPass ();
500 needRecalcMaxSize
: Boolean;
507 // reset all 'laywrap' flags for controls, set initial 'startsize'
508 for f
:= 0 to High(ctlist
) do
510 ctlist
[f
].didWrap
:= false;
511 ctlist
[f
].startsize
:= ctlist
[f
].ctl
.getDefSize
;
512 mr
:= ctlist
[f
].ctl
.getMargins
;
513 ctlist
[f
].margins
:= mr
;
514 ctlist
[f
].startsize
.w
+= mr
.horiz
;
515 ctlist
[f
].startsize
.h
+= mr
.vert
;
518 calcMaxSizeInternal(0); // this also sets `tempFlex`
519 // find max size for group, adjust 'startsize' controls to group max size
520 needRecalcMaxSize
:= false;
521 for gtype
:= 0 to 1 do
523 for f
:= 0 to High(groups
[gtype
]) do
525 grp
:= @groups
[gtype
][f
];
527 for c
:= 0 to High(grp
.ctls
) do
530 if (maxsz
< ctlist
[cidx
].startsize
[gtype
]) then maxsz
:= ctlist
[cidx
].startsize
[gtype
];
532 for c
:= 0 to High(grp
.ctls
) do
535 if (maxsz
<> ctlist
[cidx
].startsize
[gtype
]) then
537 needRecalcMaxSize
:= true;
538 ctlist
[cidx
].startsize
[gtype
] := maxsz
;
543 // recalc maxsize if necessary
544 if (needRecalcMaxSize
) then calcMaxSizeInternal(0);
545 // set "desired size" to "start size"
546 for f
:= 0 to High(ctlist
) do ctlist
[f
].desiredsize
:= ctlist
[f
].startsize
;
549 //writeln('=== calculated max size ===');
554 procedure TFlexLayouterBase
.fixLine (me
: PLayControl
; i0
, i1
: LayControlIdx
; cury
: Integer; var spaceLeft
: Single; var flexTotal
: Integer; var flexBoxCount
: Integer);
563 curx
:= me
.margins
.left
;
565 // calc minimal line height
570 lineh
:= nmax(lineh
, lc
.startsize
.h
);
571 i0
:= lc
.nextSibling
;
573 // distribute space, expand/align
578 osz
:= lc
.desiredsize
;
579 lc
.desiredsize
:= lc
.startsize
;
580 lc
.desiredpos
.x
:= curx
;
581 lc
.desiredpos
.y
:= cury
;
582 curx
+= lc
.desiredsize
.w
;
584 if (lc
.tempFlex
> 0) and (spaceLeft
> 0) then
586 toadd
:= trunc(spaceLeft
*lc
.tempFlex
/flexTotal
+0.5);
590 lc
.desiredsize
.w
+= toadd
;
592 // compensate (crudely) rounding errors
593 if (curx
> me
.desiredsize
.w
-me
.margins
.horiz
) then begin lc
.desiredsize
.w
-= 1; curx
-= 1; end;
595 layBox(lc
.firstChild
);
599 if (lc
.expand
) then lc
.desiredsize
.h
:= nmin(lc
.maxsize
.h
, lineh
) // expand
600 else if (lc
.aligndir
> 0) then lc
.desiredpos
.y
:= cury
+(lineh
-lc
.desiredsize
.h
) // bottom align
601 else if (lc
.aligndir
= 0) then lc
.desiredpos
.y
:= cury
+(lineh
-lc
.desiredsize
.h
) div 2; // center
602 if (not osz
.equals(lc
.desiredsize
)) then
604 if (lc
.inGroup
) then groupElementChanged
:= true;
606 layBox(lc
.firstChild
);
608 i0
:= lc
.nextSibling
;
612 spaceLeft
:= me
.desiredsize
.w
-me
.margins
.horiz
;
616 // do box layouting; call `layBox()` recursively if necessary
617 procedure TFlexLayouterBase
.layBox (boxidx
: LayControlIdx
);
620 flexTotal
: Integer; // total sum of flex fields
621 flexBoxCount
: Integer; // number of boxes
624 maxwdt
, maxhgt
: Integer;
625 lineStartIdx
: LayControlIdx
;
631 if (boxidx
< 0) or (boxidx
>= Length(ctlist
)) then exit
;
632 me
:= @ctlist
[boxidx
];
634 // if we have no children, there's nothing to do
635 if (me
.firstChild
= -1) then exit
;
637 // first, layout all children
638 for lc
in forChildren(boxidx
) do layBox(lc
.myidx
);
640 // second, layout lines, distribute flex data
641 if (me
.horizBox
) then
644 cury
:= me
.margins
.top
;
647 fixLine(me
, -1, -1, cury
, spaceLeft
, flexTotal
, flexBoxCount
); //HACK!
649 lineStartIdx
:= me
.firstChild
;
651 for lc
in forChildren(boxidx
) do
654 doWrap
:= (not lc
.firstInLine
) and (lc
.lineStart
);
656 if (not doWrap
) and (lc
.canWrap
) and (lc
.canWrap
) and (lc
.desiredsize
.w
> 0) and (spaceLeft
< lc
.desiredsize
.w
) then doWrap
:= true;
659 // new line, fix this one
660 if (not lc
.didWrap
) then
662 wrappingChanged
:= true;
665 fixLine(me
, lineStartIdx
, lc
.myidx
, cury
, spaceLeft
, flexTotal
, flexBoxCount
);
667 lineStartIdx
:= lc
.myidx
;
673 wrappingChanged
:= true;
677 spaceLeft
-= lc
.desiredsize
.w
;
678 if (maxhgt
< lc
.desiredsize
.h
) then maxhgt
:= lc
.desiredsize
.h
;
679 if (lc
.tempFlex
> 0) then
681 flexTotal
+= lc
.tempFlex
;
686 fixLine(me
, lineStartIdx
, -1, cury
, spaceLeft
, flexTotal
, flexBoxCount
);
694 spaceLeft
:= me
.desiredsize
.h
-me
.margins
.vert
;
697 for lc
in forChildren(boxidx
) do
699 spaceLeft
-= lc
.desiredsize
.h
;
700 if (maxwdt
< lc
.desiredsize
.w
) then maxwdt
:= lc
.desiredsize
.w
;
701 if (lc
.tempFlex
> 0) then
703 flexTotal
+= lc
.tempFlex
;
709 cury
:= me
.margins
.top
;
710 //writeln('me: ', boxidx, '; margins: ', me.margins.toString);
711 for lc
in forChildren(boxidx
) do
713 osz
:= lc
.desiredsize
;
714 lc
.desiredsize
:= lc
.startsize
;
715 lc
.desiredpos
.x
:= me
.margins
.left
;
716 lc
.desiredpos
.y
:= cury
;
717 cury
+= lc
.desiredsize
.h
;
719 if (lc
.tempFlex
> 0) and (spaceLeft
> 0) then
721 toadd
:= trunc(spaceLeft
*lc
.tempFlex
/flexTotal
+0.5);
725 lc
.desiredsize
.h
+= toadd
;
727 // compensate (crudely) rounding errors
728 if (cury
> me
.desiredsize
.h
-me
.margins
.vert
) then begin lc
.desiredsize
.h
-= 1; cury
-= 1; end;
732 if (lc
.expand
) then lc
.desiredsize
.w
:= nmin(lc
.maxsize
.w
, me
.desiredsize
.w
-me
.margins
.vert
) // expand
733 else if (lc
.aligndir
> 0) then lc
.desiredpos
.x
:= me
.desiredsize
.w
-me
.margins
.right
-lc
.desiredsize
.w
// right align
734 else if (lc
.aligndir
= 0) then lc
.desiredpos
.x
:= (me
.desiredsize
.w
-me
.margins
.horiz
-lc
.desiredsize
.w
) div 2; // center
735 if (not osz
.equals(lc
.desiredsize
)) then
737 if (lc
.inGroup
) then groupElementChanged
:= true;
739 layBox(lc
.firstChild
);
748 calcluate desired sizes (process flexes) using 'startsize', set 'desiredsize' and 'desiredpos'
749 if control has children, call 'second pass' recursively with this control as parent
751 'group-element-changed', if any group element size was changed
752 'wrapping-changed', if not 'firsttime', and wrapping was changed (i.e. first pass will not set the flag)
754 procedure TFlexLayouterBase
.secondPass ();
757 groupElementChanged
:= false;
758 wrappingChanged
:= false;
760 if (Length(ctlist
) > 0) then
762 ctlist
[0].desiredpos
:= TLayPos
.Create(0, 0);
766 // fix 'wrapping-changed' flag
767 if (firstTime
) then begin wrappingChanged
:= false; firstTime
:= false; end;
773 if 'group-element-changed':
774 for each group: adjust controls to max desired size (startsize), set 'temp-flex' flags to 0 for 'em, set 'second-again' flag
775 for other controls: if 'desiredsize' > 'maxsize', set 'startsize' to 'maxsize', set 'temp-flex' flag to 0, set 'second-again' flag
776 if 'second-again' or 'wrapping-changed':
778 reset 'wrapping-changed'
782 procedure TFlexLayouterBase
.thirdPass ();
784 secondAgain
: Boolean;
795 secondAgain
:= false;
796 if (groupElementChanged
) then
799 // find max size for group, adjust 'startsize' controls to group max size
800 for gtype
:= 0 to 1 do
802 for f
:= 0 to High(groups
[gtype
]) do
804 grp
:= @groups
[gtype
][f
];
806 for c
:= 0 to High(grp
.ctls
) do
810 ct
.expand
:= false; // don't expand grouped controls anymore
811 if (maxsz
< ct
.startsize
[gtype
]) then maxsz
:= ct
.startsize
[gtype
];
813 for c
:= 0 to High(grp
.ctls
) do
817 ct
.startsize
[gtype
] := maxsz
;
818 ct
.desiredsize
[gtype
] := maxsz
;
819 ct
.tempFlex
:= 0; // don't change control size anymore
826 for f
:= 0 to High(ctlist
) do
831 ct
.expand
:= false; // don't expand grouped controls anymore
832 ct
.tempFlex
:= 0; // don't change control size anymore
836 if (ct
.maxsize
[c
] <= 0) then continue
;
837 if (ct
.desiredsize
[c
] > ct
.maxsize
[c
]) then
839 //writeln('ctl #', f, '; dimension #', c, ': desired=', ctlist[f].desiredsize[c], '; max=', ctlist[f].maxsize[c]);
840 ct
.startsize
[c
] := ct
.maxsize
[c
];
841 ct
.desiredsize
[c
] := ct
.maxsize
[c
];
842 ct
.tempFlex
:= 0; // don't change control size anymore
848 if (not secondAgain
) and (not wrappingChanged
) then break
;
856 set 'actualsize' and 'actualpos' to 'desiredsize' and 'desiredpos'
859 procedure TFlexLayouterBase
.fourthPass ();
863 for f
:= 0 to High(ctlist
) do
865 ctlist
[f
].ctl
.setActualSizePos(ctlist
[f
].desiredpos
, ctlist
[f
].desiredsize
);
870 procedure TFlexLayouterBase
.layout ();
879 procedure TFlexLayouterBase
.dumpFlat ();
885 for f
:= 0 to High(ctlist
) do
888 ds
:= lc
.ctl
.getDefSize
;
889 ms
:= lc
.ctl
.getMaxSize
;
890 writeln(lc
.myidx
, ': startsize:', lc
.startsize
.toString(), '; desiredsize=', lc
.desiredsize
.toString(), '; maxsize=', lc
.maxsize
.toString(), '; tempFlex=', lc
.tempFlex
, '; flags=', lc
.flags
,
891 '; parent=', lc
.parent
, '; next=', lc
.nextSibling
, '; child=', lc
.firstChild
, '; ctl.size=', ds
.toString(), '; ctl.maxsize=', ms
.toString());
896 procedure TFlexLayouterBase
.dumpList (cidx
: LayControlIdx
; indent
: Integer);
904 for f
:= 0 to indent
do write(' ');
905 writeln(lc
.myidx
, ': startsize:', lc
.startsize
.toString
, '; desiredsize=', lc
.desiredsize
.toString
, '; maxsize=', lc
.maxsize
.toString
, '; tempFlex=', lc
.tempFlex
, '; despos=', lc
.desiredpos
.toString
);
906 dumpList(lc
.firstChild
, indent
+2);
907 cidx
:= lc
.nextSibling
;
912 procedure TFlexLayouterBase
.dump ();
918 // ////////////////////////////////////////////////////////////////////////// //
921 auto win := new GuiControl();
922 (win ~= new GuiControl()).mSize := TLaySize(10, 5);
923 (win ~= new GuiControl()).mSize := TLaySize(16, 8);
925 //win.mSize := TLaySize(40, 20);
927 auto lay := TFlexLayouterBase!GuiControl();
930 writeln('============================');
933 writeln('=== initial ===');
936 //lay.calcMaxSizeInternal(0);
939 writeln('=== after first pass ===');
943 writeln('=== after second pass ===');
947 writeln('=== final ===');