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 expand (): Boolean; inline;
143 function firstInLine (): Boolean; inline;
146 property didWrap
: Boolean read getDidWrap write setDidWrap
;
149 PLayGroup
= ^TLayGroup
;
152 ctls
: array of LayControlIdx
;
155 TLayCtlArray
= array of TLayControl
;
156 TLayGrpArray
= array of TLayGroup
;
159 ctlist
: TLayCtlArray
;
160 groups
: array[0..1] of TLayGrpArray
; // horiz, vert
163 groupElementChanged
: Boolean;
164 wrappingChanged
: Boolean;
167 procedure fixFlags (cidx
: LayControlIdx
);
168 procedure doChildren (parent
: LayControlIdx
; child
: ControlT
);
169 procedure appendToGroup (const gname
: AnsiString;cidx
: LayControlIdx
;gidx
: Integer);
170 procedure setupGroups ();
172 // this also sets `tempFlex`
173 procedure calcMaxSizeInternal (cidx
: LayControlIdx
);
175 procedure fixLine (me
: PLayControl
; i0
, i1
: LayControlIdx
; cury
: Integer; var spaceLeft
: Single; var flexTotal
: Integer; var flexBoxCount
: Integer);
176 // do box layouting; call `layBox()` recursively if necessary
177 procedure layBox (boxidx
: LayControlIdx
);
179 procedure firstPass ();
180 procedure secondPass ();
181 procedure thirdPass ();
182 procedure fourthPass ();
184 procedure dumpList (cidx
: LayControlIdx
; indent
: Integer);
188 TChildrenEnumerator
= record
194 constructor Create (constref actls
: TLayCtlArray
; acur
: Integer);
195 function moveNext (): Boolean; inline;
196 function getCurrent (): PLayControl
; inline;
197 function getEnumerator (): TChildrenEnumerator
; inline;
198 property current
: PLayControl read getCurrent
;
202 constructor Create ();
203 destructor Destroy (); override;
208 // build control and group lists
209 procedure setup (root
: ControlT
);
211 function forChildren (cidx
: LayControlIdx
): TChildrenEnumerator
; inline;
215 procedure dumpFlat ();
226 // ////////////////////////////////////////////////////////////////////////// //
227 procedure TFlexLayouterBase
.TLayControl
.initialize (); inline;
229 FillChar(self
, 0, sizeof(self
));
235 function TFlexLayouterBase
.TLayControl
.horizBox (): Boolean; inline; begin result
:= ((flags
and FlagHorizBox
) <> 0); end;
236 function TFlexLayouterBase
.TLayControl
.lineStart (): Boolean; inline; begin result
:= ((flags
and FlagLineStart
) <> 0); end;
237 function TFlexLayouterBase
.TLayControl
.canWrap (): Boolean; inline; begin result
:= ((flags
and FlagLineCanWrap
) <> 0); end;
238 function TFlexLayouterBase
.TLayControl
.inGroup (): Boolean; inline; begin result
:= ((flags
and FlagInGroup
) <> 0); end;
239 function TFlexLayouterBase
.TLayControl
.expand (): Boolean; inline; begin result
:= ((flags
and FlagExpand
) <> 0); end;
240 function TFlexLayouterBase
.TLayControl
.firstInLine (): Boolean; inline; begin result
:= ((flags
and FlagLineFirst
) <> 0); end;
242 function TFlexLayouterBase
.TLayControl
.getDidWrap (): Boolean; inline; begin result
:= ((flags
and FlagLineDidWrap
) <> 0); end;
243 procedure TFlexLayouterBase
.TLayControl
.setDidWrap (v
: Boolean); inline; begin if (v
) then flags
:= flags
or FlagLineDidWrap
else flags
:= flags
and (not FlagLineDidWrap
); end;
246 // ////////////////////////////////////////////////////////////////////////// //
247 constructor TFlexLayouterBase
.TChildrenEnumerator
.Create (constref actls
: TLayCtlArray
; acur
: Integer);
254 function TFlexLayouterBase
.TChildrenEnumerator
.moveNext (): Boolean; inline;
258 if (cur
>= 0) and (cur
< Length(ctls
)) then cur
:= ctls
[cur
].firstChild
else cur
:= -1;
263 cur
:= ctls
[cur
].nextSibling
;
265 result
:= (cur
>= 0);
268 function TFlexLayouterBase
.TChildrenEnumerator
.getCurrent (): PLayControl
; inline;
270 result
:= @ctls
[cur
];
273 function TFlexLayouterBase
.TChildrenEnumerator
.getEnumerator (): TChildrenEnumerator
; inline;
279 // ////////////////////////////////////////////////////////////////////////// //
280 constructor TFlexLayouterBase
.Create ();
287 groupElementChanged
:= false;
288 wrappingChanged
:= false;
292 destructor TFlexLayouterBase
.Destroy ();
299 function TFlexLayouterBase
.forChildren (cidx
: LayControlIdx
): TChildrenEnumerator
; inline;
301 result
:= TChildrenEnumerator
.Create(ctlist
, cidx
);
305 procedure TFlexLayouterBase
.clear ();
313 procedure TFlexLayouterBase
.fixFlags (cidx
: LayControlIdx
);
317 assert((cidx
>= 0) and (cidx
< Length(ctlist
)));
320 if (lc
.ctl
.isHorizBox
) then lc
.flags
:= lc
.flags
or FlagHorizBox
;
321 if (lc
.ctl
.isLineStart
) then lc
.flags
:= lc
.flags
or FlagLineStart
;
322 if (lc
.ctl
.canWrap
) then lc
.flags
:= lc
.flags
or FlagLineCanWrap
;
323 if (lc
.ctl
.getExpand
) then lc
.flags
:= lc
.flags
or FlagExpand
;
324 lc
.aligndir
:= lc
.ctl
.getAlign
;
328 procedure TFlexLayouterBase
.doChildren (parent
: LayControlIdx
; child
: ControlT
);
330 cidx
: LayControlIdx
= -1;
333 assert((parent
>= 0) and (parent
< Length(ctlist
)));
334 assert(ctlist
[parent
].firstChild
= -1);
335 while (child
<> nil) do
338 SetLength(ctlist
, Length(ctlist
)+1);
339 lc
:= @ctlist
[High(ctlist
)];
343 cidx
:= LayControlIdx(High(ctlist
));
344 ctlist
[parent
].firstChild
:= cidx
;
345 // first child is always linestart
346 lc
.flags
:= lc
.flags
or FlagLineStart
or FlagLineFirst
;
350 ctlist
[cidx
].nextSibling
:= LayControlIdx(High(ctlist
));
351 cidx
:= LayControlIdx(High(ctlist
));
357 doChildren(cidx
, child
.firstChild
);
358 child
:= child
.nextSibling
;
363 procedure TFlexLayouterBase
.appendToGroup (const gname
: AnsiString; cidx
: LayControlIdx
; gidx
: Integer);
367 if (Length(gname
) = 0) then exit
;
368 assert((cidx
>= 0) and (cidx
< Length(ctlist
)));
369 assert((gidx
= 0) or (gidx
= 1));
370 ctlist
[cidx
].flags
:= ctlist
[cidx
].flags
or FlagInGroup
;
371 for f
:= 0 to High(groups
[gidx
]) do
373 if (groups
[gidx
][f
].name
= gname
) then
375 SetLength(groups
[gidx
][f
].ctls
, Length(groups
[gidx
][f
].ctls
)+1);
376 groups
[gidx
][f
].ctls
[High(groups
[gidx
][f
].ctls
)] := cidx
;
381 f
:= Length(groups
[gidx
]);
382 SetLength(groups
[gidx
], f
+1);
383 groups
[gidx
][f
].name
:= gname
;
384 SetLength(groups
[gidx
][f
].ctls
, Length(groups
[gidx
][f
].ctls
)+1);
385 groups
[gidx
][f
].ctls
[High(groups
[gidx
][f
].ctls
)] := cidx
;
389 procedure TFlexLayouterBase
.setupGroups ();
394 for idx
:= 0 to High(ctlist
) do
397 appendToGroup(lc
.ctl
.getHGroup
, LayControlIdx(idx
), 0);
398 appendToGroup(lc
.ctl
.getVGroup
, LayControlIdx(idx
), 1);
403 // build control and group lists
404 procedure TFlexLayouterBase
.setup (root
: ControlT
);
407 if (root
= nil) then exit
;
410 SetLength(ctlist
, 1);
411 ctlist
[0].initialize();
412 ctlist
[0].myidx
:= 0;
413 ctlist
[0].ctl
:= root
;
415 ctlist
[0].flags
:= ctlist
[0].flags
or FlagLineStart
or FlagLineFirst
;
416 doChildren(0, root
.firstChild
);
425 // this also sets `tempFlex`
426 procedure TFlexLayouterBase
.calcMaxSizeInternal (cidx
: LayControlIdx
);
431 curwdt
, curhgt
, totalhgt
: Integer;
434 if (cidx
< 0) or (cidx
>= Length(ctlist
)) then exit
;
437 msz
:= lc
.ctl
.getMaxSize
;
438 negw
:= (lc
.startsize
.w
<= 0);
439 negh
:= (lc
.startsize
.h
<= 0);
441 lc
.tempFlex
:= lc
.ctl
.getFlex
;
443 for c
in forChildren(cidx
) do calcMaxSizeInternal(c
.myidx
);
445 if (lc
.horizBox
) then
448 if (negw
) then lc
.tempFlex
:= 0; // size is negative: don't expand
449 curwdt
:= lc
.margins
.horiz
;
450 curhgt
:= lc
.margins
.vert
;
452 for c
in forChildren(cidx
) do
455 doWrap
:= (not c
.firstInLine
) and (c
.lineStart
);
457 if (not doWrap
) and (lc
.canWrap
) and (c
.canWrap
) and (msz
.w
> 0) and (curwdt
+c
.startsize
.w
> lc
.startsize
.w
) then doWrap
:= true;
461 if (lc
.startsize
.w
< curwdt
) then lc
.startsize
.w
:= curwdt
;
465 curwdt
+= c
.startsize
.w
;
466 if (curhgt
< c
.startsize
.h
) then curhgt
:= c
.startsize
.h
;
469 if (lc
.startsize
.w
< curwdt
) then lc
.startsize
.w
:= curwdt
;
470 if (lc
.startsize
.h
< totalhgt
) then lc
.startsize
.h
:= totalhgt
;
475 if (negh
) then lc
.tempFlex
:= 0; // size is negative: don't expand
476 curhgt
:= lc
.margins
.vert
;
477 for c
in forChildren(cidx
) do
479 if (lc
.startsize
.w
< c
.startsize
.w
+lc
.margins
.horiz
) then lc
.startsize
.w
:= c
.startsize
.w
+lc
.margins
.horiz
;
480 curhgt
+= c
.startsize
.h
;
482 if (lc
.startsize
.h
< curhgt
) then lc
.startsize
.h
:= curhgt
;
484 if (lc
.startsize
.w
< 0) then lc
.startsize
.w
:= 0;
485 if (lc
.startsize
.h
< 0) then lc
.startsize
.h
:= 0;
487 if (lc
.maxsize
.w
< lc
.startsize
.w
) then lc
.maxsize
.w
:= lc
.startsize
.w
;
488 if (lc
.maxsize
.h
< lc
.startsize
.h
) then lc
.maxsize
.h
:= lc
.startsize
.h
;
492 procedure TFlexLayouterBase
.firstPass ();
495 needRecalcMaxSize
: Boolean;
502 // reset all 'laywrap' flags for controls, set initial 'startsize'
503 for f
:= 0 to High(ctlist
) do
505 ctlist
[f
].didWrap
:= false;
506 ctlist
[f
].startsize
:= ctlist
[f
].ctl
.getDefSize
;
507 mr
:= ctlist
[f
].ctl
.getMargins
;
508 ctlist
[f
].margins
:= mr
;
509 ctlist
[f
].startsize
.w
+= mr
.horiz
;
510 ctlist
[f
].startsize
.h
+= mr
.vert
;
513 calcMaxSizeInternal(0); // this also sets `tempFlex`
514 // find max size for group, adjust 'startsize' controls to group max size
515 needRecalcMaxSize
:= false;
516 for gtype
:= 0 to 1 do
518 for f
:= 0 to High(groups
[gtype
]) do
520 grp
:= @groups
[gtype
][f
];
522 for c
:= 0 to High(grp
.ctls
) do
525 if (maxsz
< ctlist
[cidx
].startsize
[gtype
]) then maxsz
:= ctlist
[cidx
].startsize
[gtype
];
527 for c
:= 0 to High(grp
.ctls
) do
530 if (maxsz
<> ctlist
[cidx
].startsize
[gtype
]) then
532 needRecalcMaxSize
:= true;
533 ctlist
[cidx
].startsize
[gtype
] := maxsz
;
538 // recalc maxsize if necessary
539 if (needRecalcMaxSize
) then calcMaxSizeInternal(0);
540 // set "desired size" to "start size"
541 for f
:= 0 to High(ctlist
) do ctlist
[f
].desiredsize
:= ctlist
[f
].startsize
;
544 //writeln('=== calculated max size ===');
549 procedure TFlexLayouterBase
.fixLine (me
: PLayControl
; i0
, i1
: LayControlIdx
; cury
: Integer; var spaceLeft
: Single; var flexTotal
: Integer; var flexBoxCount
: Integer);
558 curx
:= me
.margins
.left
;
560 // calc minimal line height
565 lineh
:= nmax(lineh
, lc
.startsize
.h
);
566 i0
:= lc
.nextSibling
;
568 // distribute space, expand/align
573 osz
:= lc
.desiredsize
;
574 lc
.desiredsize
:= lc
.startsize
;
575 lc
.desiredpos
.x
:= curx
;
576 lc
.desiredpos
.y
:= cury
;
577 curx
+= lc
.desiredsize
.w
;
579 if (lc
.tempFlex
> 0) and (spaceLeft
> 0) then
581 toadd
:= trunc(spaceLeft
*lc
.tempFlex
/flexTotal
+0.5);
585 lc
.desiredsize
.w
+= toadd
;
587 // compensate (crudely) rounding errors
588 if (curx
> me
.desiredsize
.w
-me
.margins
.horiz
) then begin lc
.desiredsize
.w
-= 1; curx
-= 1; end;
590 layBox(lc
.firstChild
);
594 if (lc
.expand
) then lc
.desiredsize
.h
:= nmin(lc
.maxsize
.h
, lineh
) // expand
595 else if (lc
.aligndir
> 0) then lc
.desiredpos
.y
:= cury
+(lineh
-lc
.desiredsize
.h
) // bottom align
596 else if (lc
.aligndir
= 0) then lc
.desiredpos
.y
:= cury
+(lineh
-lc
.desiredsize
.h
) div 2; // center
597 if (not osz
.equals(lc
.desiredsize
)) then
599 if (lc
.inGroup
) then groupElementChanged
:= true;
601 layBox(lc
.firstChild
);
603 i0
:= lc
.nextSibling
;
607 spaceLeft
:= me
.desiredsize
.w
-me
.margins
.horiz
;
611 // do box layouting; call `layBox()` recursively if necessary
612 procedure TFlexLayouterBase
.layBox (boxidx
: LayControlIdx
);
615 flexTotal
: Integer; // total sum of flex fields
616 flexBoxCount
: Integer; // number of boxes
619 maxwdt
, maxhgt
: Integer;
620 lineStartIdx
: LayControlIdx
;
626 if (boxidx
< 0) or (boxidx
>= Length(ctlist
)) then exit
;
627 me
:= @ctlist
[boxidx
];
629 // if we have no children, there's nothing to do
630 if (me
.firstChild
= -1) then exit
;
632 // first, layout all children
633 for lc
in forChildren(boxidx
) do layBox(lc
.myidx
);
635 // second, layout lines, distribute flex data
636 if (me
.horizBox
) then
639 cury
:= me
.margins
.top
;
642 fixLine(me
, -1, -1, cury
, spaceLeft
, flexTotal
, flexBoxCount
); //HACK!
644 lineStartIdx
:= me
.firstChild
;
646 for lc
in forChildren(boxidx
) do
649 doWrap
:= (not lc
.firstInLine
) and (lc
.lineStart
);
651 if (not doWrap
) and (lc
.canWrap
) and (lc
.canWrap
) and (lc
.desiredsize
.w
> 0) and (spaceLeft
< lc
.desiredsize
.w
) then doWrap
:= true;
654 // new line, fix this one
655 if (not lc
.didWrap
) then
657 wrappingChanged
:= true;
660 fixLine(me
, lineStartIdx
, lc
.myidx
, cury
, spaceLeft
, flexTotal
, flexBoxCount
);
662 lineStartIdx
:= lc
.myidx
;
668 wrappingChanged
:= true;
672 spaceLeft
-= lc
.desiredsize
.w
;
673 if (maxhgt
< lc
.desiredsize
.h
) then maxhgt
:= lc
.desiredsize
.h
;
674 if (lc
.tempFlex
> 0) then
676 flexTotal
+= lc
.tempFlex
;
681 fixLine(me
, lineStartIdx
, -1, cury
, spaceLeft
, flexTotal
, flexBoxCount
);
689 spaceLeft
:= me
.desiredsize
.h
-me
.margins
.vert
;
692 for lc
in forChildren(boxidx
) do
694 spaceLeft
-= lc
.desiredsize
.h
;
695 if (maxwdt
< lc
.desiredsize
.w
) then maxwdt
:= lc
.desiredsize
.w
;
696 if (lc
.tempFlex
> 0) then
698 flexTotal
+= lc
.tempFlex
;
704 cury
:= me
.margins
.top
;
705 //writeln('me: ', boxidx, '; margins: ', me.margins.toString);
706 for lc
in forChildren(boxidx
) do
708 osz
:= lc
.desiredsize
;
709 lc
.desiredsize
:= lc
.startsize
;
710 lc
.desiredpos
.x
:= me
.margins
.left
;
711 lc
.desiredpos
.y
:= cury
;
712 cury
+= lc
.desiredsize
.h
;
714 if (lc
.tempFlex
> 0) and (spaceLeft
> 0) then
716 toadd
:= trunc(spaceLeft
*lc
.tempFlex
/flexTotal
+0.5);
720 lc
.desiredsize
.h
+= toadd
;
722 // compensate (crudely) rounding errors
723 if (cury
> me
.desiredsize
.h
-me
.margins
.vert
) then begin lc
.desiredsize
.h
-= 1; cury
-= 1; end;
727 if (lc
.expand
) then lc
.desiredsize
.w
:= nmin(lc
.maxsize
.w
, me
.desiredsize
.w
-me
.margins
.vert
) // expand
728 else if (lc
.aligndir
> 0) then lc
.desiredpos
.x
:= me
.desiredsize
.w
-me
.margins
.right
-lc
.desiredsize
.w
// right align
729 else if (lc
.aligndir
= 0) then lc
.desiredpos
.x
:= (me
.desiredsize
.w
-me
.margins
.horiz
-lc
.desiredsize
.w
) div 2; // center
730 if (not osz
.equals(lc
.desiredsize
)) then
732 if (lc
.inGroup
) then groupElementChanged
:= true;
734 layBox(lc
.firstChild
);
743 calcluate desired sizes (process flexes) using 'startsize', set 'desiredsize' and 'desiredpos'
744 if control has children, call 'second pass' recursively with this control as parent
746 'group-element-changed', if any group element size was changed
747 'wrapping-changed', if not 'firsttime', and wrapping was changed (i.e. first pass will not set the flag)
749 procedure TFlexLayouterBase
.secondPass ();
752 groupElementChanged
:= false;
753 wrappingChanged
:= false;
755 if (Length(ctlist
) > 0) then
757 ctlist
[0].desiredpos
:= TLayPos
.Create(0, 0);
761 // fix 'wrapping-changed' flag
762 if (firstTime
) then begin wrappingChanged
:= false; firstTime
:= false; end;
768 if 'group-element-changed':
769 for each group: adjust controls to max desired size (startsize), set 'temp-flex' flags to 0 for 'em, set 'second-again' flag
770 for other controls: if 'desiredsize' > 'maxsize', set 'startsize' to 'maxsize', set 'temp-flex' flag to 0, set 'second-again' flag
771 if 'second-again' or 'wrapping-changed':
773 reset 'wrapping-changed'
777 procedure TFlexLayouterBase
.thirdPass ();
779 secondAgain
: Boolean;
789 secondAgain
:= false;
790 if (groupElementChanged
) then
793 // find max size for group, adjust 'startsize' controls to group max size
794 for gtype
:= 0 to 1 do
796 for f
:= 0 to High(groups
[gtype
]) do
798 grp
:= @groups
[gtype
][f
];
800 for c
:= 0 to High(grp
.ctls
) do
803 if (maxsz
< ctlist
[cidx
].startsize
[gtype
]) then maxsz
:= ctlist
[cidx
].startsize
[gtype
];
805 for c
:= 0 to High(grp
.ctls
) do
808 ctlist
[cidx
].startsize
[gtype
] := maxsz
;
809 ctlist
[cidx
].desiredsize
[gtype
] := maxsz
;
810 ctlist
[cidx
].tempFlex
:= 0; // don't change control size anymore
817 for f
:= 0 to High(ctlist
) do
821 if (ctlist
[f
].maxsize
[c
] <= 0) then continue
;
822 if (ctlist
[f
].desiredsize
[c
] > ctlist
[f
].maxsize
[c
]) then
824 //writeln('ctl #', f, '; dimension #', c, ': desired=', ctlist[f].desiredsize[c], '; max=', ctlist[f].maxsize[c]);
825 ctlist
[f
].startsize
[c
] := ctlist
[f
].maxsize
[c
];
826 ctlist
[f
].desiredsize
[c
] := ctlist
[f
].maxsize
[c
];
827 ctlist
[f
].tempFlex
:= 0; // don't change control size anymore
833 if (not secondAgain
) and (not wrappingChanged
) then break
;
841 set 'actualsize' and 'actualpos' to 'desiredsize' and 'desiredpos'
844 procedure TFlexLayouterBase
.fourthPass ();
848 for f
:= 0 to High(ctlist
) do
850 ctlist
[f
].ctl
.setActualSizePos(ctlist
[f
].desiredpos
, ctlist
[f
].desiredsize
);
855 procedure TFlexLayouterBase
.layout ();
864 procedure TFlexLayouterBase
.dumpFlat ();
870 for f
:= 0 to High(ctlist
) do
873 ds
:= lc
.ctl
.getDefSize
;
874 ms
:= lc
.ctl
.getMaxSize
;
875 writeln(lc
.myidx
, ': startsize:', lc
.startsize
.toString(), '; desiredsize=', lc
.desiredsize
.toString(), '; maxsize=', lc
.maxsize
.toString(), '; tempFlex=', lc
.tempFlex
, '; flags=', lc
.flags
,
876 '; parent=', lc
.parent
, '; next=', lc
.nextSibling
, '; child=', lc
.firstChild
, '; ctl.size=', ds
.toString(), '; ctl.maxsize=', ms
.toString());
881 procedure TFlexLayouterBase
.dumpList (cidx
: LayControlIdx
; indent
: Integer);
889 for f
:= 0 to indent
do write(' ');
890 writeln(lc
.myidx
, ': startsize:', lc
.startsize
.toString
, '; desiredsize=', lc
.desiredsize
.toString
, '; maxsize=', lc
.maxsize
.toString
, '; tempFlex=', lc
.tempFlex
, '; despos=', lc
.desiredpos
.toString
);
891 dumpList(lc
.firstChild
, indent
+2);
892 cidx
:= lc
.nextSibling
;
897 procedure TFlexLayouterBase
.dump ();
903 // ////////////////////////////////////////////////////////////////////////// //
906 auto win := new GuiControl();
907 (win ~= new GuiControl()).mSize := TLaySize(10, 5);
908 (win ~= new GuiControl()).mSize := TLaySize(16, 8);
910 //win.mSize := TLaySize(40, 20);
912 auto lay := TFlexLayouterBase!GuiControl();
915 writeln('============================');
918 writeln('=== initial ===');
921 //lay.calcMaxSizeInternal(0);
924 writeln('=== after first pass ===');
928 writeln('=== after second pass ===');
932 writeln('=== final ===');