DEADSOFTWARE

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