haxe/ui/layouts/DefaultLayout.hx
package haxe.ui.layouts;
import haxe.ui.core.Component;
import haxe.ui.geom.Size;
class DefaultLayout extends Layout {
@:clonable private var _calcFullWidths:Bool = false;
@:clonable private var _calcFullHeights:Bool = false;
@:clonable private var _roundFullWidths:Bool = false;
private function buildWidthRoundingMap():Map<Component, Int> {
if (_roundFullWidths == false || component.childComponents.length <= 1) {
return null;
}
var map:Map<Component, Int> = null;
var hasNonFullWidth:Bool = false;
for (child in component.childComponents) {
if (child.includeInLayout == false) {
continue;
}
if (child.percentWidth == null || child.percentWidth != 100) {
hasNonFullWidth = true;
break;
}
}
if (hasNonFullWidth == false) {
var remainderWidth = usableWidth % component.childComponents.length;
if (remainderWidth != 0) {
map = new Map<Component, Int>();
for (child in component.childComponents) {
if (child.includeInLayout == false) {
continue;
}
var n = 0;
if (remainderWidth > 0) {
n = 1;
remainderWidth--;
}
map.set(child, n);
}
}
}
return map;
}
private override function resizeChildren() {
var usableSize:Size = usableSize;
var percentWidth:Float = 100;
var percentHeight:Float = 100;
var fullWidthValue:Float = 100;
var fullHeightValue:Float = 100;
if (_calcFullWidths == true || _calcFullHeights == true) {
var n1 = 0;
var n2 = 0;
for (child in component.childComponents) {
if (child.includeInLayout == false) {
continue;
}
if (_calcFullWidths == true && child.percentWidth != null && child.percentWidth == 100) {
n1++;
}
if (_calcFullHeights == true && child.percentHeight != null && child.percentHeight == 100) {
n2++;
}
}
if (n1 > 0) {
fullWidthValue = 100 / n1;
}
if (n2 > 0) {
fullHeightValue = 100 / n2;
}
}
// not all backends will work (nicely) with sub pixels (heaps, openfl, etc)
// so we'll add a small optimization here that if all the items are 100%
// then we'll round them up / down to ensure we always get single pixel
// sizes (no fractions), this makes things look _much_ nicer without
// making the whole UI look bad from using subpixels, which cases nasty
// drawing artifacts in most cases
var childRoundingWidth:Map<Component, Int> = buildWidthRoundingMap();
for (child in component.childComponents) {
if (child.includeInLayout == false) {
continue;
}
var cx:Null<Float> = null;
var cy:Null<Float> = null;
if (child.percentWidth != null) {
var childPercentWidth = child.percentWidth;
if (childPercentWidth == 100) {
childPercentWidth = fullWidthValue;
}
cx = (usableSize.width * childPercentWidth) / percentWidth - marginLeft(child) - marginRight(child);
if (childRoundingWidth != null && childRoundingWidth.exists(child)) {
var roundDirection = childRoundingWidth.get(child);
if (roundDirection == 0) {
cx = Math.ffloor(cx);
} else if (roundDirection == 1) {
cx = Math.fceil(cx);
}
}
/*
#if debug
if (_component.autoWidth && usableSize.width <= 0) {
trace("WARNING: trying to use a % width in a child (id: " + child.id + ") with autosized parent (id: " + _component.id + ")");
}
#end
*/
}
if (child.percentHeight != null) {
var childPercentHeight = child.percentHeight;
if (childPercentHeight == 100) {
childPercentHeight = fullHeightValue;
}
cy = (usableSize.height * childPercentHeight) / percentHeight - marginTop(child) - marginBottom(child);
/*
#if debug
if (_component.autoHeight && usableSize.height <= 0) {
trace("WARNING: trying to use a % height in a child (id: " + child.id + ") with autosized parent (id: " + _component.id + ")");
}
#end
*/
}
if (fixedMinWidth(child) && child.percentWidth != null) {
percentWidth -= child.percentWidth;
}
if (fixedMinHeight(child) && child.percentHeight != null) {
percentHeight -= child.percentHeight;
}
child.resizeComponent(cx, cy);
}
}
private override function repositionChildren() {
var usableSize = this.usableSize;
for (child in component.childComponents) {
if (child.includeInLayout == false) {
continue;
}
repositionChild(child);
}
}
private function repositionChild(child:Component) {
if (child == null) {
return;
}
var xpos:Float = 0;
var ypos:Float = 0;
switch (horizontalAlign(child)) {
case "center":
xpos = ((usableSize.width - child.componentWidth) / 2) + paddingLeft + marginLeft(child) - marginRight(child);
case "right":
xpos = component.componentWidth - (child.componentWidth + paddingRight + marginRight(child));
default: //left
xpos = paddingLeft + marginLeft(child);
}
switch (verticalAlign(child)) {
case "center":
ypos = ((usableSize.height - child.componentHeight) / 2) + paddingTop + marginTop(child) - marginBottom(child);
case "bottom":
ypos = component.componentHeight - (child.componentHeight + paddingBottom + marginBottom(child));
default: //top
ypos = paddingTop + marginTop(child);
}
child.moveComponent(xpos, ypos);
}
}