com.ardor3d.extension.ui.layout.RowLayout Maven / Gradle / Ivy
/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at .
*/
package com.ardor3d.extension.ui.layout;
import java.util.List;
import com.ardor3d.extension.ui.UIComponent;
import com.ardor3d.extension.ui.UIContainer;
import com.ardor3d.math.Rectangle2;
import com.ardor3d.scenegraph.Spatial;
import com.google.common.collect.Lists;
/**
* This layout places components in either a horizontal or vertical row, ordered as they are placed in their container.
* Depending on settings, the layout may also take any extra space in the container and divide it up equally among child
* components that are marked as "layout resizeable".
*/
public class RowLayout extends UILayout {
private static final int MAX_RELAX = 50;
private final boolean _horizontal;
private final boolean _expandsHorizontally;
private final boolean _expandsVertically;
/**
* Construct a new RowLayout
*
* @param horizontal
* true if we should lay out horizontally, false if vertically
*/
public RowLayout(final boolean horizontal) {
this(horizontal, true, true);
}
/**
* Construct a new RowLayout
*
* @param horizontal
* true if we should lay out horizontally, false if vertically
* @param expandsHorizontally
* true (the default) if horizontal free space in the container should be divided up among the child
* components.
* @param expandsVertically
* true (the default) if vertical free space in the container should be divided up among the child
* components.
*/
public RowLayout(final boolean horizontal, final boolean expandsHorizontally, final boolean expandsVertically) {
_horizontal = horizontal;
_expandsHorizontally = expandsHorizontally;
_expandsVertically = expandsVertically;
}
/**
* @return true if we lay out horizontally, false if vertically
*/
public boolean isHorizontal() {
return _horizontal;
}
/**
* @return true (the default) if horizontal free space in the container should be divided up among the child
* components.
*/
public boolean isExpandsHorizontally() {
return _expandsHorizontally;
}
/**
*
* @return true (the default) if vertical free space in the container should be divided up among the child
* components.
*/
public boolean isExpandsVertically() {
return _expandsVertically;
}
@Override
public void layoutContents(final UIContainer container) {
final List content = container.getChildren();
if (content == null) {
return;
}
final Rectangle2 storeA = Rectangle2.fetchTempInstance();
final Rectangle2 storeB = Rectangle2.fetchTempInstance();
// Grab a list of components, squeezing them down to their min size on the flow axis
List comps = Lists.newArrayList();
List compsBack = Lists.newArrayList();
for (int i = 0; i < content.size(); i++) {
final Spatial spat = content.get(i);
if (spat instanceof UIComponent) {
final UIComponent comp = (UIComponent) spat;
final Rectangle2 rect = comp.getRelativeComponentBounds(storeA);
final Rectangle2 minRect = comp.getRelativeMinComponentBounds(storeB);
if (_horizontal) {
comp.fitComponentIn(minRect.getWidth(), rect.getHeight());
} else {
comp.fitComponentIn(rect.getWidth(), minRect.getHeight());
}
comps.add(comp);
}
}
// if we have components to layout...
if (!comps.isEmpty()) {
// Determine how much space we feel we need.
final int reqSpace = _horizontal ? getSumOfAllWidths(content) : getSumOfAllHeights(content);
// How much extra space do we have?
int freeSpace = (_horizontal ? container.getContentWidth() : container.getContentHeight()) - reqSpace;
int relaxIndex = 0;
// cycle through until we've given away all of the space
while ((freeSpace > 0 || relaxIndex == 0) && !comps.isEmpty() && relaxIndex < RowLayout.MAX_RELAX) {
final int extraPerComp = freeSpace / comps.size();
while (!comps.isEmpty()) {
final UIComponent comp = comps.remove(0);
Rectangle2 rect = comp.getRelativeComponentBounds(storeA);
final Rectangle2 origRect = storeB.set(rect);
if (freeSpace < 0) {
freeSpace = 0;
}
if (_horizontal) {
final int height = _expandsVertically ? container.getContentHeight() : rect.getHeight();
final int width = (_expandsHorizontally ? extraPerComp : 0) + rect.getWidth();
if (height == rect.getHeight() && width == rect.getWidth()) {
continue;
}
comp.fitComponentIn(width, height);
rect = comp.getRelativeComponentBounds(storeA);
if (Math.abs(rect.getWidth() - width) <= 1) {
compsBack.add(comp);
}
freeSpace -= rect.getWidth() - origRect.getWidth();
} else {
final int width = _expandsHorizontally ? container.getContentWidth() : rect.getWidth();
final int height = (_expandsVertically ? extraPerComp : 0) + rect.getHeight();
if (height == rect.getHeight() && width == rect.getWidth()) {
continue;
}
comp.fitComponentIn(width, height);
rect = comp.getRelativeComponentBounds(storeA);
if (Math.abs(rect.getHeight() - height) <= 1) {
compsBack.add(comp);
}
freeSpace -= rect.getHeight() - origRect.getHeight();
}
}
final List compsTemp = comps;
comps = compsBack;
compsBack = compsTemp;
relaxIndex++;
}
int x = 0;
int y = !_expandsVertically && !_horizontal ? container.getContentHeight() - reqSpace : 0;
// Now, go through children and set proper location.
for (int i = 0; i < content.size(); i++) {
final Spatial spat = _horizontal ? content.get(i) : content.get(content.size() - i - 1);
if (!(spat instanceof UIComponent)) {
continue;
}
final UIComponent comp = (UIComponent) spat;
final Rectangle2 rect = comp.getRelativeComponentBounds(storeA);
if (_horizontal) {
comp.setLocalXY(x - rect.getX(), Math.max(container.getContentHeight() / 2 - rect.getHeight() / 2
- rect.getY(), 0));
x += rect.getWidth();
} else {
comp.setLocalXY(Math.max(container.getContentWidth() / 2 - rect.getWidth() / 2 - rect.getX(), 0), y
- rect.getY());
y += rect.getHeight();
}
}
}
Rectangle2.releaseTempInstance(storeA);
Rectangle2.releaseTempInstance(storeB);
}
@Override
public void updateMinimumSizeFromContents(final UIContainer container) {
int minW = 0, minH = 0;
if (container.getNumberOfChildren() > 0) {
final List content = container.getChildren();
// compute the min width and height of the container
final Rectangle2 store = new Rectangle2();
for (final Spatial s : content) {
if (!(s instanceof UIComponent)) {
continue;
}
final UIComponent comp = (UIComponent) s;
final Rectangle2 rect = comp.getRelativeMinComponentBounds(store);
if (_horizontal) {
minW += rect.getWidth();
if (minH < rect.getHeight()) {
minH = rect.getHeight();
}
} else {
if (minW < rect.getWidth()) {
minW = rect.getWidth();
}
minH += rect.getHeight();
}
}
}
container.setMinimumContentSize(minW, minH);
}
private int getSumOfAllHeights(final List content) {
int sum = 0;
if (content != null) {
final Rectangle2 store = new Rectangle2();
for (final Spatial spat : content) {
if (spat instanceof UIComponent) {
final Rectangle2 rect = ((UIComponent) spat).getRelativeMinComponentBounds(store);
sum += rect.getHeight();
}
}
}
return sum;
}
private int getSumOfAllWidths(final List content) {
int sum = 0;
if (content != null) {
final Rectangle2 store = new Rectangle2();
for (final Spatial spat : content) {
if (spat instanceof UIComponent) {
final Rectangle2 rect = ((UIComponent) spat).getRelativeMinComponentBounds(store);
sum += rect.getWidth();
}
}
}
return sum;
}
}