summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 7817748)
raw | patch | inline | side by side (parent: 7817748)
author | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Mon, 2 Oct 2017 18:47:02 +0000 (21:47 +0300) | ||
committer | Ketmar Dark <ketmar@ketmar.no-ip.org> | |
Mon, 2 Oct 2017 23:34:42 +0000 (02:34 +0300) |
padding is inserted *after* control, if there is another control coming, and current
control doesn't have "nopad" property set
note that span control has no padding by default, and box with frame has no padding too
control doesn't have "nopad" property set
note that span control has no padding by default, and box with frame has no padding too
src/flexui/fui_ctls.pas | patch | blob | history | |
src/flexui/fui_flexlay.pas | patch | blob | history |
index 8c3834e37bc3f6f79c3624a99cfd7a49e661fe9f..b7d5bfa2a27dca30cc392399161d64abca808b53 100644 (file)
--- a/src/flexui/fui_ctls.pas
+++ b/src/flexui/fui_ctls.pas
mLayDefSize: TLaySize;
mLayMaxSize: TLaySize;
mFullSize: TLaySize;
+ mNoPad: Boolean;
+ mPadding: TLaySize;
public
// layouter interface
function getDefSize (): TLaySize; inline; // default size; <0: use max size
//procedure setDefSize (const sz: TLaySize); inline; // default size; <0: use max size
function getMargins (): TLayMargins; inline;
+ function getPadding (): TLaySize; inline; // children padding (each non-first child will get this on left/top)
function getMaxSize (): TLaySize; inline; // max size; <0: set to some huge value
//procedure setMaxSize (const sz: TLaySize); inline; // max size; <0: set to some huge value
function getFlex (): Integer; inline; // <=0: not flexible
function isHorizBox (): Boolean; inline; // horizontal layout for children?
- procedure setHorizBox (v: Boolean); inline; // horizontal layout for children?
function canWrap (): Boolean; inline; // for horizontal boxes: can wrap children? for child: `false` means 'nonbreakable at *next* ctl'
- procedure setCanWrap (v: Boolean); inline; // for horizontal boxes: can wrap children? for child: `false` means 'nonbreakable at *next* ctl'
+ function noPad (): Boolean; inline; // ignore padding in box direction for this control
function isLineStart (): Boolean; inline; // `true` if this ctl should start a new line; ignored for vertical boxes
- procedure setLineStart (v: Boolean); inline; // `true` if this ctl should start a new line; ignored for vertical boxes
function getAlign (): Integer; inline; // aligning in non-main direction: <0: left/up; 0: center; >0: right/down
- procedure setAlign (v: Integer); inline; // aligning in non-main direction: <0: left/up; 0: center; >0: right/down
function getExpand (): Boolean; inline; // expanding in non-main direction: `true` will ignore align and eat all available space
- procedure setExpand (v: Boolean); inline; // expanding in non-main direction: `true` will ignore align and eat all available space
function getHGroup (): AnsiString; inline; // empty: not grouped
- procedure setHGroup (const v: AnsiString); inline; // empty: not grouped
function getVGroup (): AnsiString; inline; // empty: not grouped
- procedure setVGroup (const v: AnsiString); inline; // empty: not grouped
procedure setActualSizePos (constref apos: TLayPos; constref asize: TLaySize); inline;
property flex: Integer read mFlex write mFlex;
property flDefaultSize: TLaySize read mDefSize write mDefSize;
property flMaxSize: TLaySize read mMaxSize write mMaxSize;
- property flHoriz: Boolean read isHorizBox write setHorizBox;
- property flCanWrap: Boolean read canWrap write setCanWrap;
- property flLineStart: Boolean read isLineStart write setLineStart;
- property flAlign: Integer read getAlign write setAlign;
- property flExpand: Boolean read getExpand write setExpand;
- property flHGroup: AnsiString read getHGroup write setHGroup;
- property flVGroup: AnsiString read getVGroup write setVGroup;
+ property flPadding: TLaySize read mPadding write mPadding;
+ property flHoriz: Boolean read mHoriz write mHoriz;
+ property flCanWrap: Boolean read mCanWrap write mCanWrap;
+ property flLineStart: Boolean read mLineStart write mLineStart;
+ property flAlign: Integer read mAlign write mAlign;
+ property flExpand: Boolean read mExpand write mExpand;
+ property flHGroup: AnsiString read mHGroup write mHGroup;
+ property flVGroup: AnsiString read mVGroup write mVGroup;
+ property flNoPad: Boolean read mNoPad write mNoPad;
property fullSize: TLaySize read mFullSize;
protected
function parsePos (par: TTextParser): TLayPos;
function parseSize (par: TTextParser): TLaySize;
+ function parsePadding (par: TTextParser): TLaySize;
+ function parseHPadding (par: TTextParser; def: Integer): TLaySize;
+ function parseVPadding (par: TTextParser; def: Integer): TLaySize;
function parseBool (par: TTextParser): Boolean;
function parseAnyAlign (par: TTextParser): Integer;
function parseHAlign (par: TTextParser): Integer;
//mDefSize := TLaySize.Create(64, 8); // default size
mDefSize := TLaySize.Create(0, 0); // default size
mMaxSize := TLaySize.Create(-1, -1); // maximum size
+ mPadding := TLaySize.Create(0, 0);
+ mNoPad := false;
mFlex := 0;
mHoriz := true;
mCanWrap := false;
// ////////////////////////////////////////////////////////////////////////// //
function TUIControl.getDefSize (): TLaySize; inline; begin result := mLayDefSize; end;
function TUIControl.getMaxSize (): TLaySize; inline; begin result := mLayMaxSize; end;
+function TUIControl.getPadding (): TLaySize; inline; begin result := mPadding; end;
function TUIControl.getFlex (): Integer; inline; begin result := mFlex; end;
function TUIControl.isHorizBox (): Boolean; inline; begin result := mHoriz; end;
-procedure TUIControl.setHorizBox (v: Boolean); inline; begin mHoriz := v; end;
function TUIControl.canWrap (): Boolean; inline; begin result := mCanWrap; end;
-procedure TUIControl.setCanWrap (v: Boolean); inline; begin mCanWrap := v; end;
+function TUIControl.noPad (): Boolean; inline; begin result := mNoPad; end;
function TUIControl.isLineStart (): Boolean; inline; begin result := mLineStart; end;
-procedure TUIControl.setLineStart (v: Boolean); inline; begin mLineStart := v; end;
function TUIControl.getAlign (): Integer; inline; begin result := mAlign; end;
-procedure TUIControl.setAlign (v: Integer); inline; begin mAlign := v; end;
function TUIControl.getExpand (): Boolean; inline; begin result := mExpand; end;
-procedure TUIControl.setExpand (v: Boolean); inline; begin mExpand := v; end;
function TUIControl.getHGroup (): AnsiString; inline; begin result := mHGroup; end;
-procedure TUIControl.setHGroup (const v: AnsiString); inline; begin mHGroup := v; end;
function TUIControl.getVGroup (): AnsiString; inline; begin result := mVGroup; end;
-procedure TUIControl.setVGroup (const v: AnsiString); inline; begin mVGroup := v; end;
-
-function TUIControl.getMargins (): TLayMargins; inline;
-begin
- result := TLayMargins.Create(mFrameHeight, mFrameWidth, mFrameHeight, mFrameWidth);
-end;
+function TUIControl.getMargins (): TLayMargins; inline; begin result := TLayMargins.Create(mFrameHeight, mFrameWidth, mFrameHeight, mFrameWidth); end;
procedure TUIControl.setActualSizePos (constref apos: TLayPos; constref asize: TLaySize); inline;
begin
par.expectDelim(ech);
end;
+function TUIControl.parsePadding (par: TTextParser): TLaySize;
+begin
+ result := parseSize(par);
+end;
+
+function TUIControl.parseHPadding (par: TTextParser; def: Integer): TLaySize;
+begin
+ if (par.isInt) then
+ begin
+ result.h := def;
+ result.w := par.expectInt();
+ end
+ else
+ begin
+ result := parsePadding(par);
+ end;
+end;
+
+function TUIControl.parseVPadding (par: TTextParser; def: Integer): TLaySize;
+begin
+ if (par.isInt) then
+ begin
+ result.w := def;
+ result.h := par.expectInt();
+ end
+ else
+ begin
+ result := parsePadding(par);
+ end;
+end;
+
function TUIControl.parseBool (par: TTextParser): Boolean;
begin
result :=
if (strEquCI1251(prname, 'defheight')) or (strEquCI1251(prname, 'height')) then begin mDefSize.h := par.expectInt(); exit; end;
if (strEquCI1251(prname, 'maxwidth')) then begin mMaxSize.w := par.expectInt(); exit; end;
if (strEquCI1251(prname, 'maxheight')) then begin mMaxSize.h := par.expectInt(); exit; end;
+ // padding
+ if (strEquCI1251(prname, 'padding')) then begin mPadding := parsePadding(par); exit; end;
+ if (strEquCI1251(prname, 'nopad')) then begin mNoPad := true; exit; end;
// flags
if (strEquCI1251(prname, 'wrap')) then begin mCanWrap := parseBool(par); exit; end;
if (strEquCI1251(prname, 'linestart')) then begin mLineStart := parseBool(par); exit; end;
begin
mHasFrame := v;
if (mHasFrame) then begin mFrameWidth := 8; mFrameHeight := 8; end else begin mFrameWidth := 0; mFrameHeight := 0; end;
+ if (mHasFrame) then mNoPad := true;
end;
function TUIBox.parseProperty (const prname: AnsiString; par: TTextParser): Boolean;
begin
if (parseOrientation(prname, par)) then begin result := true; exit; end;
+ if (strEquCI1251(prname, 'padding')) then
+ begin
+ if (mHoriz) then mPadding := parseHPadding(par, 0) else mPadding := parseVPadding(par, 0);
+ result := true;
+ exit;
+ end;
if (strEquCI1251(prname, 'frame')) then
begin
setHasFrame(parseBool(par));
inherited;
mExpand := true;
mCanFocus := false;
+ mNoPad := true;
mCtl4Style := 'span';
end;
index 99db4fe02e8adf31de7f2770e0a49ed8110957bf..fb470c4fee5a71a41fd198e1b251b8466841ea3a 100644 (file)
procedure layPrepare (); // called before registering control in layouter
function getDefSize (): TLaySize; // default size; <0: use max size
function getMargins (): TLayMargins;
+ function getPadding (): TLaySize; // children padding (each non-first child will get this on left/top)
function getMaxSize (): TLaySize; // max size; <0: set to some huge value
function getFlex (): Integer; // <=0: not flexible
function isHorizBox (): Boolean; // horizontal layout for children?
function canWrap (): Boolean; // for horizontal boxes: can wrap children? for child: `false` means 'nonbreakable at *next* ctl'
+ function noPad (): Boolean; // ignore padding in box direction for this control
function isLineStart (): Boolean; // `true` if this ctl should start a new line; ignored for vertical boxes
function getAlign (): Integer; // aligning in non-main direction: <0: left/up; 0: center; >0: right/down
function getExpand (): Boolean; // expanding in non-main direction: `true` will ignore align and eat all available space
FlagHorizBox = LongWord(1) shl 0; // horizontal layout for children
FlagLineStart = LongWord(1) shl 1;
FlagLineCanWrap = LongWord(1) shl 2;
+ FlagNoPad = LongWord(1) shl 3;
// internal
- FlagLineDidWrap = LongWord(1) shl 3; // will be set when line was wrapped
- FlagInGroup = LongWord(1) shl 4; // set if this control is a member of any group
- FlagExpand = LongWord(1) shl 5;
- FlagLineFirst = LongWord(1) shl 6;
+ FlagLineDidWrap = LongWord(1) shl 8; // will be set when line was wrapped
+ FlagInGroup = LongWord(1) shl 9; // set if this control is a member of any group
+ FlagExpand = LongWord(1) shl 10;
+ FlagLineFirst = LongWord(1) shl 11;
private
type
public
myidx: LayControlIdx;
tempFlex: Integer;
- flags: LongWord; // see below
+ flags: LongWord; // see above
aligndir: Integer;
startsize: TLaySize; // current
desiredsize: TLaySize;
maxsize: TLaySize;
margins: TLayMargins; // can never be negative
+ padding: TLaySize;
desiredpos: TLayPos;
ctl: ControlT;
parent: LayControlIdx; // = -1;
function canWrap (): Boolean; inline;
function inGroup (): Boolean; inline;
function firstInLine (): Boolean; inline;
+ function noPad (): Boolean; inline;
function getExpand (): Boolean; inline;
procedure setExpand (v: Boolean); inline;
// this also sets `tempFlex`
procedure calcMaxSizeInternal (cidx: LayControlIdx);
- procedure fixLine (me: PLayControl; i0, i1: LayControlIdx; var cury: Integer; var spaceLeft: Single);
+ procedure fixLine (me: PLayControl; i0, i1: LayControlIdx; ypad: Integer; var cury: Integer; var spaceLeft: Single);
// do box layouting; call `layBox()` recursively if necessary
procedure layBox (boxidx: LayControlIdx);
@@ -201,6 +206,7 @@ function TFlexLayouterBase.TLayControl.lineStart (): Boolean; inline; begin resu
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.firstInLine (): Boolean; inline; begin result := ((flags and FlagLineFirst) <> 0); end;
+function TFlexLayouterBase.TLayControl.noPad (): Boolean; inline; begin result := ((flags and FlagNoPad) <> 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;
if (lc.ctl.isLineStart) then lc.flags := lc.flags or FlagLineStart;
if (lc.ctl.canWrap) then lc.flags := lc.flags or FlagLineCanWrap;
if (lc.ctl.getExpand) then lc.flags := lc.flags or FlagExpand;
+ if (lc.ctl.noPad) then lc.flags := lc.flags or FlagNoPad;
lc.aligndir := lc.ctl.getAlign;
end;
zerow: Boolean;
curwdt, curhgt, totalhgt: Integer;
doWrap: Boolean;
+ xpad, ypad: Integer;
+ realpad: Integer;
+ dopad: Boolean = false;
begin
if (cidx < 0) or (cidx >= Length(ctlist)) then exit;
curwdt := lc.margins.horiz;
curhgt := 0;
totalhgt := lc.margins.vert;
+ xpad := nmax(0, lc.padding.w);
+ ypad := 0;
for c in forChildren(cidx) do
begin
+ if (dopad) then realpad := xpad else realpad := 0;
// new line?
doWrap := (not c.firstInLine) and (c.lineStart);
// need to wrap?
- 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 (not doWrap) and (not zerow) and (not negw) and (lc.canWrap) and (c.canWrap) and (msz.w > 0) and (curwdt+c.startsize.w+realpad > lc.startsize.w) then doWrap := true;
if (doWrap) then
begin
totalhgt += curhgt;
if (lc.startsize.w < curwdt) then lc.startsize.w := curwdt;
curwdt := 0;
curhgt := 0;
+ ypad := nmax(0, lc.padding.h);
+ realpad := 0;
end;
- curwdt += c.startsize.w;
- if (curhgt < c.startsize.h) then curhgt := c.startsize.h;
+ curwdt += c.startsize.w+realpad;
+ if (curhgt < c.startsize.h+ypad) then curhgt := c.startsize.h+ypad;
+ dopad := (xpad > 0) and (not lc.noPad);
end;
//writeln('00: ', cidx, ': totalhgt=', totalhgt);
totalhgt += curhgt;
// vertical boxes
if (negh) then lc.tempFlex := 0; // size is negative: don't expand
curhgt := lc.margins.vert;
+ ypad := nmax(0, lc.padding.h);
for c in forChildren(cidx) do
begin
if (lc.startsize.w < c.startsize.w+lc.margins.horiz) then lc.startsize.w := c.startsize.w+lc.margins.horiz;
curhgt += c.startsize.h;
+ if (dopad) then curhgt += ypad;
+ dopad := (not c.noPad);
end;
if (lc.startsize.h < curhgt) then lc.startsize.h := curhgt;
end;
maxsz: Integer;
cidx: LayControlIdx;
ct: PLayControl;
- mr: TLayMargins;
begin
// reset all 'laywrap' flags for controls, set initial 'startsize'
for f := 0 to High(ctlist) do
begin
ctlist[f].didWrap := false;
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;
+ ctlist[f].margins := ctlist[f].ctl.getMargins;
+ ctlist[f].padding := ctlist[f].ctl.getPadding;
end;
// setup sizes
calcMaxSizeInternal(0); // this also sets `tempFlex`
end;
-procedure TFlexLayouterBase.fixLine (me: PLayControl; i0, i1: LayControlIdx; var cury: Integer; var spaceLeft: Single);
+procedure TFlexLayouterBase.fixLine (me: PLayControl; i0, i1: LayControlIdx; ypad: Integer; var cury: Integer; var spaceLeft: Single);
var
flexTotal: Integer = 0; // total sum of flex fields
flexBoxCount: Integer = 0; // number of boxes
toadd: Integer;
sti0: Integer;
lineh: Integer;
+ xpad: Integer;
begin
+ if (ypad < 0) then ypad := 0;
curx := me.margins.left;
sti0 := i0;
// calc minimal line height, count flexboxes
while (i0 <> i1) do
begin
lc := @ctlist[i0];
- lineh := nmax(lineh, lc.startsize.h);
+ lineh := nmax(lineh, lc.startsize.h+ypad);
if (lc.tempFlex > 0) then begin flexTotal += lc.tempFlex; flexBoxCount += 1; end;
i0 := lc.nextSibling;
end;
// distribute space, expand/align
+ xpad := nmax(0, me.padding.w);
i0 := sti0;
while (i0 <> i1) do
begin
lc.desiredpos.x := curx;
lc.desiredpos.y := cury;
curx += lc.desiredsize.w;
+ if (xpad > 0) and (not lc.noPad) then curx += xpad;
// fix flexbox size
if (lc.tempFlex > 0) and (spaceLeft > 0) then
begin
doWrap: Boolean;
toadd: Integer;
osz: TLaySize;
+ xpad, ypad, realpad: Integer;
+ dopad: Boolean = false;
begin
if (boxidx < 0) or (boxidx >= Length(ctlist)) then exit;
me := @ctlist[boxidx];
begin
// horizontal boxes
cury := me.margins.top;
+ xpad := nmax(0, me.padding.w);
+ ypad := 0;
- fixLine(me, -1, -1, cury, spaceLeft); //HACK!
+ fixLine(me, -1, -1, 0, cury, spaceLeft); //HACK!
lineStartIdx := me.firstChild;
for lc in forChildren(boxidx) do
begin
+ if (dopad) then realpad := xpad else realpad := 0;
// new line?
doWrap := (not lc.firstInLine) and (lc.lineStart);
// need to wrap?
- if (not doWrap) and (lc.canWrap) and (lc.canWrap) and (lc.desiredsize.w > 0) and (spaceLeft < lc.desiredsize.w) then doWrap := true;
+ if (not doWrap) and (lc.canWrap) and (lc.canWrap) and (lc.desiredsize.w > 0) and (spaceLeft-realpad < lc.desiredsize.w) then doWrap := true;
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);
+ fixLine(me, lineStartIdx, lc.myidx, ypad, cury, spaceLeft);
lineStartIdx := lc.myidx;
+ ypad := nmax(0, me.padding.h);
+ realpad := 0;
end
else
begin
if (lc.didWrap) then begin wrappingChanged := true; lc.didWrap := false; end;
end;
- spaceLeft -= lc.desiredsize.w;
+ spaceLeft -= lc.desiredsize.w+realpad;
+ dopad := (xpad > 0) and (not lc.noPad);
//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);
+ fixLine(me, lineStartIdx, -1, ypad, cury, spaceLeft);
end
else
begin
flexTotal := 0;
flexBoxCount := 0;
spaceLeft := me.desiredsize.h-me.margins.vert;
+ ypad := nmax(0, me.padding.h);
// calc flex
for lc in forChildren(boxidx) do
lc.desiredpos.x := me.margins.left;
lc.desiredpos.y := cury;
cury += lc.desiredsize.h;
+ if (ypad > 0) and (not lc.noPad) then cury += ypad;
// fix flexbox size
if (lc.tempFlex > 0) and (spaceLeft > 0) then
begin