DEADSOFTWARE

changed license to GPLv3 only; sorry, no trust to FSF anymore
[d2df-sdl.git] / src / flexui / fui_flexlay.pas
1 (* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit fui_flexlay;
18 (*
19 control default size will be increased by margins
20 negative margins are ignored
21 ControlT:
22 procedure layPrepare (); // called before registering control in layouter
23 function getDefSize (): TLaySize; // default size; <0: use max size
24 function getMargins (): TLayMargins;
25 function getPadding (): TLaySize; // children padding (each non-first child will get this on left/top)
26 function getMaxSize (): TLaySize; // max size; <0: set to some huge value
27 function getFlex (): Integer; // <=0: not flexible
28 function isHorizBox (): Boolean; // horizontal layout for children?
29 function noPad (): Boolean; // ignore padding in box direction for this control
30 function getAlign (): Integer; // aligning in non-main direction: <0: left/up; 0: center; >0: right/down
31 function getExpand (): Boolean; // expanding in non-main direction: `true` will ignore align and eat all available space
32 procedure setActualSizePos (constref apos: TLayPos; constref asize: TLaySize);
33 function getHGroup (): AnsiString; // empty: not grouped
34 function getVGroup (): AnsiString; // empty: not grouped
35 function nextSibling (): ControlT;
36 function firstChild (): ControlT;
37 *)
39 interface
41 uses
42 fui_common;
45 // ////////////////////////////////////////////////////////////////////////// //
46 type
47 generic TFlexLayouterBase<ControlT> = class
48 public
49 type CtlT = ControlT;
51 private
52 type LayControlIdx = Integer;
54 private
55 // flags
56 const
57 FlagHorizBox = LongWord(1) shl 0; // horizontal layout for children
58 FlagNoPad = LongWord(1) shl 1;
59 FlagExpand = LongWord(1) shl 2;
60 // internal
61 FlagInGroupH = LongWord(1) shl 8; // set if this control is a member of any group
62 FlagInGroupV = LongWord(1) shl 9; // set if this control is a member of any group
64 private
65 type
66 PLayControl = ^TLayControl;
67 TLayControl = record
68 public
69 myidx: LayControlIdx;
70 tempFlex: Integer;
71 flags: LongWord; // see above
72 aligndir: Integer;
73 startsize: TLaySize; // original size
74 desiredsize: TLaySize; // current size
75 maxsize: TLaySize; // current maximum size
76 margins: TLayMargins; // can never be negative
77 padding: TLaySize;
78 desiredpos: TLayPos;
79 ctl: ControlT;
80 parent: LayControlIdx; // = -1;
81 firstChild: LayControlIdx; // = -1;
82 nextSibling: LayControlIdx; // = -1;
84 public
85 procedure initialize (); inline;
87 function horizBox (): Boolean; inline;
88 function inGroup (idx: Integer): Boolean; inline;
89 function noPad (): Boolean; inline;
91 function getExpand (): Boolean; inline;
92 procedure setExpand (v: Boolean); inline;
94 function alignLeft (): Boolean; inline;
95 function alignTop (): Boolean; inline;
96 function alignRight (): Boolean; inline;
97 function alignBottom (): Boolean; inline;
98 function alignCenter (): Boolean; inline;
100 function visible (): Boolean; inline;
102 public
103 property expand: Boolean read getExpand write setExpand;
104 end;
106 PLayGroup = ^TLayGroup;
107 TLayGroup = record
108 name: AnsiString;
109 ctls: array of LayControlIdx;
110 end;
112 TLayCtlArray = array of TLayControl;
113 TLayGrpArray = array of TLayGroup;
115 private
116 ctlist: TLayCtlArray;
117 groups: array[0..1] of TLayGrpArray; // horiz, vert
118 groupElementChanged: Boolean;
120 private
121 procedure firstTimeSetup (cidx: LayControlIdx);
122 procedure doChildren (parent: LayControlIdx; child: ControlT);
123 procedure appendToGroup (const gname: AnsiString;cidx: LayControlIdx;gidx: Integer);
124 procedure clearGroups ();
125 procedure setupGroups ();
127 procedure distributeChildren (boxidx: LayControlIdx; maindir: Integer);
128 // do box layouting; call `layBox()` recursively if necessary
129 procedure layBox (boxidx: LayControlIdx);
131 procedure firstPass ();
132 procedure secondPass ();
133 procedure thirdPass ();
135 procedure dumpList (cidx: LayControlIdx; indent: Integer);
137 public
138 type
139 TChildrenEnumerator = record
140 private
141 ctls: TLayCtlArray;
142 cur: Integer;
143 first: Boolean;
144 onlyVisible: Boolean;
145 public
146 constructor Create (constref actls: TLayCtlArray; acur: Integer; aonlyvis: Boolean);
147 function moveNext (): Boolean; inline;
148 function getCurrent (): PLayControl; inline;
149 function getEnumerator (): TChildrenEnumerator; inline;
150 property current: PLayControl read getCurrent;
151 end;
153 public
154 constructor Create ();
155 destructor Destroy (); override;
157 // clear build lists
158 procedure clear ();
160 // build control and group lists
161 procedure setup (root: ControlT);
163 function forChildren (cidx: LayControlIdx): TChildrenEnumerator; inline;
164 function forVisibleChildren (cidx: LayControlIdx): TChildrenEnumerator; inline;
166 procedure layout ();
168 procedure dumpFlat ();
169 procedure dump ();
170 end;
173 implementation
175 uses
176 utils;
179 // ////////////////////////////////////////////////////////////////////////// //
180 procedure TFlexLayouterBase.TLayControl.initialize (); inline;
181 begin
182 FillChar(self, 0, sizeof(self));
183 parent := -1;
184 firstChild := -1;
185 nextSibling := -1;
186 end;
188 function TFlexLayouterBase.TLayControl.horizBox (): Boolean; inline; begin result := ((flags and FlagHorizBox) <> 0); end;
189 function TFlexLayouterBase.TLayControl.inGroup (idx: Integer): Boolean; inline; begin if (idx = 0) then result := ((flags and FlagInGroupH) <> 0) else if (idx = 1) then result := ((flags and FlagInGroupV) <> 0) else result := false; end;
190 function TFlexLayouterBase.TLayControl.noPad (): Boolean; inline; begin result := ((flags and FlagNoPad) <> 0); end;
192 function TFlexLayouterBase.TLayControl.getExpand (): Boolean; inline; begin result := ((flags and FlagExpand) <> 0); end;
193 procedure TFlexLayouterBase.TLayControl.setExpand (v: Boolean); inline; begin if (v) then flags := flags or FlagExpand else flags := flags and (not FlagExpand); end;
195 function TFlexLayouterBase.TLayControl.alignLeft (): Boolean; inline; begin result := (aligndir < 0); end;
196 function TFlexLayouterBase.TLayControl.alignTop (): Boolean; inline; begin result := (aligndir < 0); end;
197 function TFlexLayouterBase.TLayControl.alignRight (): Boolean; inline; begin result := (aligndir > 0); end;
198 function TFlexLayouterBase.TLayControl.alignBottom (): Boolean; inline; begin result := (aligndir > 0); end;
199 function TFlexLayouterBase.TLayControl.alignCenter (): Boolean; inline; begin result := (aligndir = 0); end;
201 function TFlexLayouterBase.TLayControl.visible (): Boolean; inline;
202 begin
203 result := (startsize.w <> 0) or (startsize.h <> 0);
204 end;
207 // ////////////////////////////////////////////////////////////////////////// //
208 constructor TFlexLayouterBase.TChildrenEnumerator.Create (constref actls: TLayCtlArray; acur: Integer; aonlyvis: Boolean);
209 begin
210 ctls := actls;
211 cur := acur;
212 first := true;
213 onlyVisible := aonlyvis;
214 end;
216 function TFlexLayouterBase.TChildrenEnumerator.moveNext (): Boolean; inline;
217 begin
218 while true do
219 begin
220 if first then
221 begin
222 if (cur >= 0) and (cur < Length(ctls)) then cur := ctls[cur].firstChild else cur := -1;
223 first := false;
224 end
225 else
226 begin
227 cur := ctls[cur].nextSibling;
228 end;
229 result := (cur >= 0);
230 if (not result) or (not onlyVisible) then break;
231 if (ctls[cur].visible) then break;
232 end;
233 end;
235 function TFlexLayouterBase.TChildrenEnumerator.getCurrent (): PLayControl; inline;
236 begin
237 result := @ctls[cur];
238 end;
240 function TFlexLayouterBase.TChildrenEnumerator.getEnumerator (): TChildrenEnumerator; inline;
241 begin
242 result := self;
243 end;
246 // ////////////////////////////////////////////////////////////////////////// //
247 constructor TFlexLayouterBase.Create ();
248 begin
249 ctlist := nil;
250 groups[0] := nil;
251 groups[1] := nil;
252 groupElementChanged := false;
253 end;
256 destructor TFlexLayouterBase.Destroy ();
257 begin
258 clear();
259 inherited;
260 end;
263 function TFlexLayouterBase.forChildren (cidx: LayControlIdx): TChildrenEnumerator; inline;
264 begin
265 result := TChildrenEnumerator.Create(ctlist, cidx, false);
266 end;
268 function TFlexLayouterBase.forVisibleChildren (cidx: LayControlIdx): TChildrenEnumerator; inline;
269 begin
270 result := TChildrenEnumerator.Create(ctlist, cidx, true);
271 end;
274 procedure TFlexLayouterBase.clear ();
275 begin
276 clearGroups();
277 ctlist := nil;
278 end;
281 procedure TFlexLayouterBase.doChildren (parent: LayControlIdx; child: ControlT);
282 var
283 cidx: LayControlIdx = -1;
284 lc: PLayControl;
285 begin
286 assert((parent >= 0) and (parent < Length(ctlist)));
287 assert(ctlist[parent].firstChild = -1);
288 while (child <> nil) do
289 begin
290 child.layPrepare;
291 //if (msz.w = 0) or (msz.h = 0) then continue; // hidden controls will have zero maxsize, so skip 'em
292 SetLength(ctlist, Length(ctlist)+1);
293 lc := @ctlist[High(ctlist)];
294 lc.initialize();
295 if (cidx = -1) then
296 begin
297 cidx := LayControlIdx(High(ctlist));
298 ctlist[parent].firstChild := cidx;
299 end
300 else
301 begin
302 ctlist[cidx].nextSibling := LayControlIdx(High(ctlist));
303 cidx := LayControlIdx(High(ctlist));
304 end;
305 lc.myidx := cidx;
306 lc.ctl := child;
307 lc.parent := parent;
308 doChildren(cidx, child.firstChild);
309 child := child.nextSibling;
310 end;
311 end;
314 procedure TFlexLayouterBase.appendToGroup (const gname: AnsiString; cidx: LayControlIdx; gidx: Integer);
315 var
316 f: Integer;
317 gflg: LongWord;
318 begin
319 if (Length(gname) = 0) then exit;
320 assert((cidx >= 0) and (cidx < Length(ctlist)));
321 assert((gidx = 0) or (gidx = 1));
322 if (gidx = 0) then gflg := FlagInGroupH else gflg := FlagInGroupV;
323 ctlist[cidx].flags := ctlist[cidx].flags or gflg;
324 for f := 0 to High(groups[gidx]) do
325 begin
326 if (groups[gidx][f].name = gname) then
327 begin
328 SetLength(groups[gidx][f].ctls, Length(groups[gidx][f].ctls)+1);
329 groups[gidx][f].ctls[High(groups[gidx][f].ctls)] := cidx;
330 exit;
331 end;
332 end;
333 // new group
334 f := Length(groups[gidx]);
335 SetLength(groups[gidx], f+1);
336 groups[gidx][f].name := gname;
337 SetLength(groups[gidx][f].ctls, Length(groups[gidx][f].ctls)+1);
338 groups[gidx][f].ctls[High(groups[gidx][f].ctls)] := cidx;
339 end;
342 procedure TFlexLayouterBase.clearGroups ();
343 var
344 gidx, f: Integer;
345 begin
346 for gidx := 0 to 1 do
347 begin
348 for f := 0 to High(groups[gidx]) do groups[gidx][f].ctls := nil;
349 groups[gidx] := nil;
350 end;
351 end;
354 procedure TFlexLayouterBase.setupGroups ();
355 var
356 gflg: LongWord;
357 idx, gidx, f, c: Integer;
358 lc: PLayControl;
359 begin
360 clearGroups();
361 for idx := 0 to High(ctlist) do
362 begin
363 lc := @ctlist[idx];
364 appendToGroup(lc.ctl.getHGroup, LayControlIdx(idx), 0);
365 appendToGroup(lc.ctl.getVGroup, LayControlIdx(idx), 1);
366 end;
367 // if control is only one in a group, mark is as "not grouped"
368 for gidx := 0 to 1 do
369 begin
370 if (gidx = 0) then gflg := LongWord(not FlagInGroupH) else gflg := LongWord(not FlagInGroupV);
371 f := 0;
372 while (f < Length(groups[gidx])) do
373 begin
374 if (Length(groups[gidx][f].ctls) < 2) then
375 begin
376 // unmark controls
377 for c := 0 to High(groups[gidx][f].ctls) do
378 begin
379 lc := @ctlist[groups[gidx][f].ctls[c]];
380 lc.flags := lc.flags and gflg;
381 end;
382 // remove this group
383 groups[gidx][f].ctls := nil;
384 for c := f+1 to High(groups[gidx]) do groups[gidx][c-1] := groups[gidx][c];
385 c := High(groups[gidx]);
386 groups[gidx][c].ctls := nil;
387 SetLength(groups[gidx], c);
388 end
389 else
390 begin
391 Inc(f);
392 end;
393 end;
394 end;
395 end;
398 // build control and group lists
399 procedure TFlexLayouterBase.setup (root: ControlT);
400 begin
401 clear();
402 if (root = nil) then exit;
403 root.layPrepare;
404 try
405 SetLength(ctlist, 1);
406 ctlist[0].initialize();
407 ctlist[0].myidx := 0;
408 ctlist[0].ctl := root;
409 doChildren(0, root.firstChild);
410 except
411 clear();
412 raise;
413 end;
414 end;
417 procedure TFlexLayouterBase.firstTimeSetup (cidx: LayControlIdx);
418 var
419 lc: PLayControl;
420 begin
421 assert((cidx >= 0) and (cidx < Length(ctlist)));
422 lc := @ctlist[cidx];
423 lc.flags := 0;
424 if (lc.ctl.isHorizBox) then lc.flags := lc.flags or FlagHorizBox;
425 if (lc.ctl.getExpand) then lc.flags := lc.flags or FlagExpand;
426 if (lc.ctl.noPad) then lc.flags := lc.flags or FlagNoPad;
427 lc.aligndir := lc.ctl.getAlign;
428 lc.startsize := lc.ctl.getDefSize;
429 //lc.startsize.w := nmax(0, lc.startsize.w);
430 //lc.startsize.h := nmax(0, lc.startsize.h);
431 lc.margins := lc.ctl.getMargins;
432 lc.margins.left := nmax(0, lc.margins.left);
433 lc.margins.top := nmax(0, lc.margins.top);
434 lc.margins.right := nmax(0, lc.margins.right);
435 lc.margins.bottom := nmax(0, lc.margins.bottom);
436 lc.padding := lc.ctl.getPadding;
437 lc.padding.w := nmax(0, lc.padding.w);
438 lc.padding.h := nmax(0, lc.padding.h);
439 lc.maxsize := TLaySize.Create(-1, -1);
440 if (lc.maxsize.w >= 0) then lc.startsize.w := nmin(lc.maxsize.w, lc.startsize.w);
441 if (lc.maxsize.h >= 0) then lc.startsize.h := nmin(lc.maxsize.h, lc.startsize.h);
442 lc.desiredsize := lc.startsize;
443 lc.tempFlex := lc.ctl.getFlex;
444 end;
447 procedure TFlexLayouterBase.firstPass ();
448 var
449 f: Integer;
450 gtype: Integer;
451 begin
452 groupElementChanged := false;
453 setupGroups();
454 for f := 0 to High(ctlist) do firstTimeSetup(f);
455 // if we have any groups, set "group element changed" flag, so third pass will fix 'em
456 for gtype := 0 to 1 do
457 begin
458 if (Length(groups[gtype]) > 0) then
459 begin
460 groupElementChanged := true;
461 break;
462 end;
463 end;
464 end;
467 procedure TFlexLayouterBase.distributeChildren (boxidx: LayControlIdx; maindir: Integer);
468 var
469 me, lc: PLayControl;
470 suppdir: Integer;
471 marg0, marg1, margtotal: Integer;
472 marg0Op, marg1Op, margtotalOp: Integer;
473 flexTotal: Integer = 0; // total sum of flex fields
474 spaceLeft: Integer = 0;
475 dopad: Boolean = false;
476 prevpad: Boolean = false;
477 maxdim: Integer = 0;
478 curpos: Integer;
479 toadd: Integer;
480 pad: Integer;
481 osz: TLaySize;
482 begin
483 assert((boxidx >= 0) and (boxidx < Length(ctlist)));
484 assert((maindir = 0) or (maindir = 1));
485 // cache some parameters
486 me := @ctlist[boxidx];
487 suppdir := 1-maindir;
488 if (maindir = 0) then
489 begin
490 marg0 := me.margins.left;
491 marg1 := me.margins.right;
492 marg0Op := me.margins.top;
493 marg1Op := me.margins.bottom;
494 end
495 else
496 begin
497 marg0 := me.margins.top;
498 marg1 := me.margins.bottom;
499 marg0Op := me.margins.left;
500 marg1Op := me.margins.right;
501 end;
502 margtotal := marg0+marg1;
503 margtotalOp := marg0Op+marg1Op;
504 // horizontal boxes
505 pad := nmax(0, me.padding[maindir]);
506 // calc required space, count flexes
507 for lc in forVisibleChildren(boxidx) do
508 begin
509 if (lc.tempFlex > 0) then flexTotal += lc.tempFlex;
510 spaceLeft += nmax(0, lc.desiredsize[maindir]);
511 // insert padding if both current and previous children allow padding
512 dopad := (not lc.noPad);
513 if (prevpad) and (dopad) then spaceLeft += pad;
514 prevpad := dopad;
515 maxdim := nmax(maxdim, lc.desiredsize[suppdir]);
516 end;
517 // add margins
518 spaceLeft += margtotal;
519 maxdim += margtotalOp;
520 // fix box size
521 me.desiredsize[maindir] := nmax(spaceLeft, me.desiredsize[maindir]);
522 me.desiredsize[suppdir] := nmax(maxdim, me.desiredsize[suppdir]);
523 // calculate free space
524 spaceLeft := me.desiredsize[maindir]-spaceLeft;
525 // distribute children
526 dopad := false;
527 prevpad := false;
528 curpos := marg0;
529 for lc in forVisibleChildren(boxidx) do
530 begin
531 osz := lc.desiredsize;
532 // main direction
533 // insert padding if both current and previous children allow padding
534 dopad := (not lc.noPad);
535 if (prevpad) and (dopad) then curpos += pad;
536 prevpad := dopad;
537 lc.desiredpos[maindir] := curpos;
538 if (lc.desiredsize[maindir] < 0) then lc.desiredsize[maindir] := 0;
539 curpos += lc.desiredsize[maindir];
540 // fix flexbox size
541 //writeln(':lcidx=', lc.myidx, '; tempFlex=', lc.tempFlex, '; spaceLeft=', spaceLeft);
542 if (spaceLeft > 0) and (lc.tempFlex > 0) then
543 begin
544 toadd := trunc(spaceLeft*lc.tempFlex/flexTotal+0.5);
545 if (toadd > 0) then
546 begin
547 // size changed
548 // compensate (crudely) rounding errors
549 if (curpos+toadd > me.desiredsize[maindir]-margtotal) then toadd -= 1;
550 //writeln('***curpos=', curpos, '; toadd=', toadd, '; spaceLeft=', spaceLeft);
551 // fix size
552 lc.desiredsize[maindir] := lc.desiredsize[maindir]+toadd;
553 curpos += toadd;
554 end;
555 end;
556 // secondary direction: expand or align
557 if (lc.desiredsize[suppdir] < 0) then lc.desiredsize[suppdir] := 0;
558 lc.desiredpos[suppdir] := marg0Op; // left/top align
559 if (lc.expand) then lc.desiredsize[suppdir] := me.desiredsize[suppdir]-margtotalOp // expand
560 else if (lc.aligndir > 0) then lc.desiredpos[suppdir] := me.desiredsize[suppdir]-marg1Op-lc.desiredsize[suppdir] // right/bottom align
561 else if (lc.aligndir = 0) then lc.desiredpos[suppdir] := (me.desiredsize[suppdir]-lc.desiredsize[suppdir]) div 2; // center
562 lc.desiredsize[suppdir] := nmax(lc.desiredsize[suppdir], osz[suppdir]);
563 // relayout children if size was changed
564 if (not osz.equals(lc.desiredsize)) then
565 begin
566 if (lc.inGroup(0)) or (lc.inGroup(1)) then groupElementChanged := true;
567 layBox(lc.myidx);
568 end;
569 end;
570 end;
573 // do box layouting; call `layBox()` recursively if necessary
574 procedure TFlexLayouterBase.layBox (boxidx: LayControlIdx);
575 var
576 me: PLayControl;
577 lc: PLayControl;
578 osz: TLaySize;
579 begin
580 if (boxidx < 0) or (boxidx >= Length(ctlist)) then exit;
581 me := @ctlist[boxidx];
582 // if we have no children, there's nothing to do
583 if (me.firstChild <> -1) then
584 begin
585 while true do
586 begin
587 osz := me.desiredsize;
588 // layout all children
589 for lc in forVisibleChildren(boxidx) do layBox(lc.myidx);
590 // distribute children
591 if (me.horizBox) then distributeChildren(me.myidx, 0) else distributeChildren(me.myidx, 1);
592 // relayout children if size was changed
593 if (osz.equals(me.desiredsize)) then break;
594 if (me.inGroup(0)) or (me.inGroup(1)) then groupElementChanged := true;
595 end;
596 end;
597 end;
600 procedure TFlexLayouterBase.secondPass ();
601 var
602 secondAgain: Boolean;
603 gtype: Integer;
604 maxsz: Integer;
605 grp: PLayGroup;
606 f, c: Integer;
607 maindir: Integer;
608 cidx: LayControlIdx;
609 ct: PLayControl;
610 loopsLeft: Integer = 64;
611 begin
612 while (loopsLeft > 0) do
613 begin
614 Dec(loopsLeft);
615 layBox(0);
616 secondAgain := false;
617 if (groupElementChanged) then
618 begin
619 secondAgain := true;
620 groupElementChanged := false;
621 // fix group sizes
622 for gtype := 0 to 1 do
623 begin
624 for f := 0 to High(groups[gtype]) do
625 begin
626 grp := @groups[gtype][f];
627 maxsz := 0;
628 for c := 0 to High(grp.ctls) do
629 begin
630 cidx := grp.ctls[c];
631 ct := @ctlist[cidx];
632 maxsz := nmax(maxsz, ct.desiredsize[gtype]);
633 end;
634 for c := 0 to High(grp.ctls) do
635 begin
636 cidx := grp.ctls[c];
637 ct := @ctlist[cidx];
638 ct.desiredsize[gtype] := maxsz;
639 end;
640 end;
641 end;
642 end;
643 // don't change group control sizes anymore
644 for f := 0 to High(ctlist) do
645 begin
646 ct := @ctlist[f];
647 if (ct.parent <> -1) then
648 begin
649 if (ctlist[ct.parent].horizBox) then maindir := 0 else maindir := 1;
650 end
651 else
652 begin
653 maindir := 0; // arbitrary
654 end;
655 if (ct.inGroup(maindir)) then ct.tempFlex := 0; // don't change control size anymore
656 if (ct.inGroup(1-maindir)) then ct.expand := false; // don't expand grouped controls anymore
657 end;
658 if (not secondAgain) then break;
659 end;
660 end;
663 procedure TFlexLayouterBase.thirdPass ();
664 var
665 f: Integer;
666 begin
667 for f := 0 to High(ctlist) do
668 begin
669 ctlist[f].ctl.setActualSizePos(ctlist[f].desiredpos, ctlist[f].desiredsize);
670 end;
671 end;
674 procedure TFlexLayouterBase.layout ();
675 begin
676 if (Length(ctlist) = 0) then exit;
677 ctlist[0].desiredpos := TLayPos.Create(0, 0);
678 firstPass();
679 //writeln('============== AFTER FIRST PASS =============='); dump();
680 secondPass();
681 //writeln('============== AFTER SECOND PASS =============='); dump();
682 thirdPass();
683 end;
686 procedure TFlexLayouterBase.dumpFlat ();
687 var
688 f: Integer;
689 lc: PLayControl;
690 ds, ms: TLaySize;
691 begin
692 for f := 0 to High(ctlist) do
693 begin
694 lc := @ctlist[f];
695 ds := lc.ctl.getDefSize;
696 ms := lc.ctl.getMaxSize;
697 writeln(lc.myidx, ': startsize:', lc.startsize.toString(), '; desiredsize=', lc.desiredsize.toString(), '; maxsize=', lc.maxsize.toString(), '; tempFlex=', lc.tempFlex, '; flags=', lc.flags,
698 '; parent=', lc.parent, '; next=', lc.nextSibling, '; child=', lc.firstChild, '; ctl.size=', ds.toString(), '; ctl.maxsize=', ms.toString());
699 end;
700 end;
703 procedure TFlexLayouterBase.dumpList (cidx: LayControlIdx; indent: Integer);
704 var
705 lc: PLayControl;
706 f: Integer;
707 begin
708 while (cidx >= 0) do
709 begin
710 lc := @ctlist[cidx];
711 for f := 0 to indent do write(' ');
712 if (not lc.visible) then write('!');
713 write(lc.myidx, ': ');
714 if (lc.ctl.id <> '') then write('<', lc.ctl.className, '> {', lc.ctl.id, '} ') else write('<', lc.ctl.className, '> ');
715 writeln('startsize:', lc.startsize.toString, '; desiredsize=', lc.desiredsize.toString, '; maxsize=', lc.maxsize.toString, '; tempFlex=', lc.tempFlex, '; despos=', lc.desiredpos.toString);
716 dumpList(lc.firstChild, indent+2);
717 cidx := lc.nextSibling;
718 end;
719 end;
722 procedure TFlexLayouterBase.dump ();
723 begin
724 dumpList(0, 0);
725 end;
728 end.