/*
* Copyright (c) 2008-2010, Matthias Mann
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Matthias Mann nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package de.matthiasmann.twl;
/**
*
* @author Matthias Mann
*/
public class BoxLayout extends Widget {
public enum Direction {
HORIZONTAL,
VERTICAL
};
private Direction direction;
private int spacing;
private boolean scroll;
private Alignment alignment = Alignment.TOP;
public BoxLayout() {
this(Direction.HORIZONTAL);
}
public BoxLayout(Direction direction) {
this.direction = direction;
}
public int getSpacing() {
return spacing;
}
public void setSpacing(int spacing) {
if(this.spacing != spacing) {
this.spacing = spacing;
invalidateLayout();
}
}
public boolean isScroll() {
return scroll;
}
public void setScroll(boolean scroll) {
if(this.scroll != scroll) {
this.scroll = scroll;
invalidateLayout();
}
}
public Alignment getAlignment() {
return alignment;
}
public void setAlignment(Alignment alignment) {
if(alignment == null) {
throw new NullPointerException("alignment");
}
if(this.alignment != alignment) {
this.alignment = alignment;
invalidateLayout();
}
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
if(direction == null) {
throw new NullPointerException("direction");
}
if(this.direction != direction) {
this.direction = direction;
invalidateLayout();
}
}
@Override
public int getMinWidth() {
int minWidth = (direction == Direction.HORIZONTAL)
? computeMinWidthHorizontal(this, spacing)
: computeMinWidthVertical(this);
return Math.max(super.getMinWidth(), minWidth + getBorderHorizontal());
}
@Override
public int getMinHeight() {
int minHeight = (direction == Direction.HORIZONTAL)
? computeMinHeightHorizontal(this)
: computeMinHeightVertical(this, spacing);
return Math.max(super.getMinHeight(), minHeight + getBorderVertical());
}
@Override
public int getPreferredInnerWidth() {
return (direction == Direction.HORIZONTAL)
? computePreferredWidthHorizontal(this, spacing)
: computePreferredWidthVertical(this);
}
@Override
public int getPreferredInnerHeight() {
return (direction == Direction.HORIZONTAL)
? computePreferredHeightHorizontal(this)
: computePreferredHeightVertical(this, spacing);
}
@Override
protected void applyTheme(ThemeInfo themeInfo) {
super.applyTheme(themeInfo);
setSpacing(themeInfo.getParameter("spacing", 0));
setAlignment(themeInfo.getParameter("alignment", Alignment.TOP));
}
public static int computeMinWidthHorizontal(Widget container, int spacing) {
final int n = container.getNumChildren();
int minWidth = Math.max(0, n-1) * spacing;
for(int i=0 ; i<n ; i++) {
minWidth += container.getChild(i).getMinWidth();
}
return minWidth;
}
public static int computeMinHeightHorizontal(Widget container) {
final int n = container.getNumChildren();
int minHeight = 0;
for(int i=0 ; i<n ; i++) {
minHeight = Math.max(minHeight, container.getChild(i).getMinHeight());
}
return minHeight;
}
public static int computePreferredWidthHorizontal(Widget container, int spacing) {
final int n = container.getNumChildren();
int prefWidth = Math.max(0, n-1) * spacing;
for(int i=0 ; i<n ; i++) {
prefWidth += getPrefChildWidth(container.getChild(i));
}
return prefWidth;
}
public static int computePreferredHeightHorizontal(Widget container) {
final int n = container.getNumChildren();
int prefHeight = 0;
for(int i=0 ; i<n ; i++) {
prefHeight = Math.max(prefHeight, getPrefChildHeight(container.getChild(i)));
}
return prefHeight;
}
public static int computeMinWidthVertical(Widget container) {
final int n = container.getNumChildren();
int minWidth = 0;
for(int i=0 ; i<n ; i++) {
minWidth = Math.max(minWidth, container.getChild(i).getMinWidth());
}
return minWidth;
}
public static int computeMinHeightVertical(Widget container, int spacing) {
final int n = container.getNumChildren();
int minHeight = Math.max(0, n-1) * spacing;
for(int i=0 ; i<n ; i++) {
minHeight += container.getChild(i).getMinHeight();
}
return minHeight;
}
public static int computePreferredWidthVertical(Widget container) {
final int n = container.getNumChildren();
int prefWidth = 0;
for(int i=0 ; i<n ; i++) {
prefWidth = Math.max(prefWidth, getPrefChildWidth(container.getChild(i)));
}
return prefWidth;
}
public static int computePreferredHeightVertical(Widget container, int spacing) {
final int n = container.getNumChildren();
int prefHeight = Math.max(0, n-1) * spacing;
for(int i=0 ; i<n ; i++) {
prefHeight += getPrefChildHeight(container.getChild(i));
}
return prefHeight;
}
public static void layoutHorizontal(Widget container, int spacing, Alignment alignment, boolean scroll) {
final int numChildren = container.getNumChildren();
final int height = container.getInnerHeight();
int x = container.getInnerX();
int y = container.getInnerY();
// 1: check if we need to scroll
if(scroll) {
int width = computePreferredWidthHorizontal(container, spacing);
if(width > container.getInnerWidth()) {
x -= width - container.getInnerWidth();
}
}
// 2: position children
for(int idx=0 ; idx<numChildren ; idx++) {
Widget child = container.getChild(idx);
int childWidth = getPrefChildWidth(child);
int childHeight = (alignment == Alignment.FILL) ? height : getPrefChildHeight(child);
int yoff = (height - childHeight) * alignment.vpos / 2;
child.setSize(childWidth, childHeight);
child.setPosition(x, y + yoff);
x += childWidth + spacing;
}
}
public static void layoutVertical(Widget container, int spacing, Alignment alignment, boolean scroll) {
final int numChildren = container.getNumChildren();
final int width = container.getInnerWidth();
int x = container.getInnerX();
int y = container.getInnerY();
// 1: check if we need to scroll
if(scroll) {
int height = computePreferredHeightVertical(container, spacing);
if(height > container.getInnerHeight()) {
x -= height - container.getInnerHeight();
}
}
// 2: position children
for(int idx=0 ; idx<numChildren ; idx++) {
Widget child = container.getChild(idx);
int childWidth = (alignment == Alignment.FILL) ? width : getPrefChildWidth(child);
int childHeight = getPrefChildHeight(child);
int xoff = (width - childWidth) * alignment.hpos / 2;
child.setSize(childWidth, childHeight);
child.setPosition(x + xoff, y);
y += childHeight + spacing;
}
}
@Override
protected void layout() {
if(getNumChildren() > 0) {
if(direction == Direction.HORIZONTAL) {
layoutHorizontal(this, spacing, alignment, scroll);
} else {
layoutVertical(this, spacing, alignment, scroll);
}
}
}
private static int getPrefChildWidth(Widget child) {
return computeSize(child.getMinWidth(), child.getPreferredWidth(), child.getMaxWidth());
}
private static int getPrefChildHeight(Widget child) {
return computeSize(child.getMinHeight(), child.getPreferredHeight(), child.getMaxHeight());
}
}