com.alee.extended.layout.FormLayout Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of weblaf-ui Show documentation
Show all versions of weblaf-ui Show documentation
WebLaf is a Java Swing Look and Feel and extended components library for cross-platform applications
/*
* This file is part of WebLookAndFeel library.
*
* WebLookAndFeel library is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebLookAndFeel library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebLookAndFeel library. If not, see .
*/
package com.alee.extended.layout;
import com.alee.utils.SwingUtils;
import javax.swing.*;
import java.awt.*;
import java.util.Map;
import java.util.WeakHashMap;
/**
* @author Mikle Garin
*/
public class FormLayout extends AbstractLayoutManager
{
/**
* Form elements constraint constants.
*/
public static final String LEFT = "left";
public static final String CENTER = "center";
public static final String RIGHT = "right";
public static final String FILL = "fill";
public static final String LINE = "line";
/**
* Whether left side of the form should fill all the free space given by container or not.
*/
protected boolean fillLeftSide;
/**
* Whether right side of the form should fill all the free space given by container or not.
*/
protected boolean fillRightSide;
/**
* Whether component in each form row on the left side of the form should fill given height or not by default.
*/
protected boolean fillLeftSideHeight;
/**
* Whether component in each form row on the right side of the form should fill given height or not by default.
*/
protected boolean fillRightSideHeight;
/**
* Default vertical alignment of components on the left side of the form.
* This is applied only if left side components do not fill the whole row height.
*/
protected int leftVerticalAlignment;
/**
* Default vertical alignment of components on the right side of the form.
* This is applied only if right side components do not fill the whole row height.
*/
protected int rightVerticalAlignment;
/**
* Horizontal gap between columns.
*/
protected int horizontalGap;
/**
* Vertical gap between rows.
*/
protected int verticalGap;
/**
* Added component constraints.
*/
protected Map layoutConstraints = new WeakHashMap ();
/**
* Constructs new FormLayout with zero gaps and filled right side.
*/
public FormLayout ()
{
this ( false, true, 0, 0 );
}
/**
* Constructs new FormLayout with zero gaps and specified left/right side fills.
*
* @param fillLeftSide whether left side of the form should fill all the free space given by container or not
* @param fillRightSide whether right side of the form should fill all the free space given by container or not
*/
public FormLayout ( final boolean fillLeftSide, final boolean fillRightSide )
{
this ( fillLeftSide, fillRightSide, 0, 0 );
}
/**
* Constructs new FormLayout with specified gaps and filled right side.
*
* @param horizontalGap horizontal gap between columns
* @param verticalGap vertical gap between rows
*/
public FormLayout ( final int horizontalGap, final int verticalGap )
{
this ( false, true, horizontalGap, verticalGap );
}
/**
* Constructs new FormLayout with specified gaps and left/right side fills.
*
* @param fillLeftSide whether left side of the form should fill all the free space given by container or not
* @param fillRightSide whether right side of the form should fill all the free space given by container or not
* @param horizontalGap horizontal gap between columns
* @param verticalGap vertical gap between rows
*/
public FormLayout ( final boolean fillLeftSide, final boolean fillRightSide, final int horizontalGap, final int verticalGap )
{
super ();
this.fillLeftSide = fillLeftSide;
this.fillRightSide = fillRightSide;
this.horizontalGap = horizontalGap;
this.verticalGap = verticalGap;
this.fillLeftSideHeight = false;
this.fillRightSideHeight = false;
this.leftVerticalAlignment = SwingConstants.CENTER;
this.rightVerticalAlignment = SwingConstants.CENTER;
}
/**
* Returns whether left side of the form should fill all the free space given by container or not.
*
* @return true if left side of the form should fill all the free space given by container, false otherwise
*/
public boolean isFillLeftSide ()
{
return fillLeftSide;
}
/**
* Sets whether left side of the form should fill all the free space given by container or not.
*
* @param fillLeftSide whether left side of the form should fill all the free space given by container or not
*/
public void setFillLeftSide ( final boolean fillLeftSide )
{
this.fillLeftSide = fillLeftSide;
}
/**
* Returns whether right side of the form should fill all the free space given by container or not.
*
* @return true if right side of the form should fill all the free space given by container, false otherwise
*/
public boolean isFillRightSide ()
{
return fillRightSide;
}
/**
* Sets whether right side of the form should fill all the free space given by container or not.
*
* @param fillRightSide whether right side of the form should fill all the free space given by container or not
*/
public void setFillRightSide ( final boolean fillRightSide )
{
this.fillRightSide = fillRightSide;
}
/**
* Returns whether component in each form row on the left side of the form should fill given height or not.
*
* @return true if component in each form row on the left side of the form should fill given height, false otherwise
*/
public boolean isFillLeftSideHeight ()
{
return fillLeftSideHeight;
}
/**
* Sets whether component in each form row on the left side of the form should fill given height or not.
*
* @param fillLeftSideHeight whether component in each form row on the left side of the form should fill given height or not
*/
public void setFillLeftSideHeight ( final boolean fillLeftSideHeight )
{
this.fillLeftSideHeight = fillLeftSideHeight;
}
/**
* Returns whether component in each form row on the right side of the form should fill given height or not.
*
* @return true if component in each form row on the right side of the form should fill given height, false otherwise
*/
public boolean isFillRightSideHeight ()
{
return fillRightSideHeight;
}
/**
* Sets whether component in each form row on the right side of the form should fill given height or not.
*
* @param fillRightSideHeight whether component in each form row on the right side of the form should fill given height or not
*/
public void setFillRightSideHeight ( final boolean fillRightSideHeight )
{
this.fillRightSideHeight = fillRightSideHeight;
}
/**
* Returns default vertical alignment of components on the left side of the form.
*
* @return default vertical alignment of components on the left side of the form
*/
public int getLeftVerticalAlignment ()
{
return leftVerticalAlignment;
}
/**
* Sets default vertical alignment of components on the left side of the form.
*
* @param leftVerticalAlignment default vertical alignment of components on the left side of the form
*/
public void setLeftVerticalAlignment ( final int leftVerticalAlignment )
{
this.leftVerticalAlignment = leftVerticalAlignment;
}
/**
* Returns default vertical alignment of components on the right side of the form.
*
* @return default vertical alignment of components on the right side of the form
*/
public int getRightVerticalAlignment ()
{
return rightVerticalAlignment;
}
/**
* Sets default vertical alignment of components on the right side of the form.
*
* @param rightVerticalAlignment default vertical alignment of components on the right side of the form
*/
public void setRightVerticalAlignment ( final int rightVerticalAlignment )
{
this.rightVerticalAlignment = rightVerticalAlignment;
}
/**
* Returns horizontal gap between columns.
*
* @return horizontal gap between columns
*/
public int getHorizontalGap ()
{
return horizontalGap;
}
/**
* Sets horizontal gap between columns.
*
* @param horizontalGap horizontal gap between columns
*/
public void setHorizontalGap ( final int horizontalGap )
{
this.horizontalGap = horizontalGap;
}
/**
* Returns vertical gap between rows.
*
* @return vertical gap between rows
*/
public int getVerticalGap ()
{
return verticalGap;
}
/**
* Sets vertical gap between rows.
*
* @param verticalGap vertical gap between rows
*/
public void setVerticalGap ( final int verticalGap )
{
this.verticalGap = verticalGap;
}
@Override
public void addComponent ( final Component component, final Object constraints )
{
// Adding default constraints if needed (left side components aligned to right, right side components fill the space)
final Container parent = component.getParent ();
final int cc = parent.getComponentCount ();
boolean wasFirstColumn = false;
for ( int j = 0; j < cc; j++ )
{
final Component c = parent.getComponent ( j );
final String lc = layoutConstraints.get ( c );
if ( lc != null )
{
wasFirstColumn = !wasFirstColumn && !lc.equals ( LINE );
}
if ( c == component )
{
final String halign = constraints != null ? "" + constraints : wasFirstColumn ? FILL : RIGHT;
layoutConstraints.put ( component, halign );
break;
}
}
}
@Override
public void removeComponent ( final Component component )
{
layoutConstraints.remove ( component );
}
@Override
public void layoutContainer ( final Container parent )
{
final int cc = parent.getComponentCount ();
if ( cc > 0 )
{
// Pre-calculating children preferred sizes
final Map cps = SwingUtils.getChildPreferredSizes ( parent );
// Calculating preferred column widths
int lpw = 0;
int rpw = 0;
boolean wasFirstColumn = false;
for ( int j = 0; j < cc; j++ )
{
final Component component = parent.getComponent ( j );
final Dimension ps = cps.get ( component );
if ( !wasFirstColumn )
{
if ( layoutConstraints.get ( component ).equals ( LINE ) )
{
// Skipping to next line
wasFirstColumn = false;
}
else
{
// First column
lpw = Math.max ( lpw, ps.width );
// Taking second column into account
wasFirstColumn = true;
}
}
else
{
// Second column
rpw = Math.max ( lpw, ps.width );
// Going to next line
wasFirstColumn = false;
}
}
// Post-calculating actual column widths
final Dimension parentSize = parent.getSize ();
final Insets i = parent.getInsets ();
if ( fillLeftSide && fillRightSide )
{
if ( cc > 1 )
{
lpw = ( parentSize.width - i.left - i.right - horizontalGap ) / 2;
rpw = lpw;
}
else
{
lpw = parentSize.width - i.left - i.right;
}
}
else if ( fillLeftSide )
{
if ( cc > 1 )
{
lpw = parentSize.width - i.left - i.right - horizontalGap - rpw;
}
else
{
lpw = parentSize.width - i.left - i.right;
}
}
else
{
if ( cc > 1 )
{
rpw = parentSize.width - i.left - i.right - horizontalGap - lpw;
}
}
// Layouting components
final int x1 = i.left;
final int x2 = i.left + lpw + horizontalGap;
final int w = parentSize.width - i.left - i.right;
int y = i.top;
wasFirstColumn = false;
for ( int j = 0; j < cc; j++ )
{
final Component component = parent.getComponent ( j );
final Dimension ps = cps.get ( component );
final String pos = layoutConstraints.get ( component );
if ( !wasFirstColumn )
{
if ( pos.equals ( LINE ) )
{
// Full line column
final int rh = ps.height;
component.setBounds ( x1, y, w, rh );
// Incrementing Y position
y += rh + verticalGap;
// Skipping to next line
wasFirstColumn = false;
}
else
{
// Row preferred height
final int next = cc > j + 1 ? cps.get ( parent.getComponent ( j + 1 ) ).height : 0;
final int rh = Math.max ( ps.height, next );
// First column
final int cy = fillLeftSideHeight ? y : getSideY ( true, y, rh, ps.height );
final int ch = fillLeftSideHeight ? rh : ps.height;
final int aw = Math.min ( ps.width, lpw );
if ( pos.equals ( LEFT ) )
{
component.setBounds ( x1, cy, aw, ch );
}
else if ( pos.equals ( CENTER ) )
{
component.setBounds ( x1 + lpw / 2 - aw / 2, cy, aw, ch );
}
else if ( pos.equals ( RIGHT ) )
{
component.setBounds ( x1 + lpw - aw, cy, aw, ch );
}
else if ( pos.equals ( FILL ) )
{
component.setBounds ( x1, cy, lpw, ch );
}
// Going for second column
wasFirstColumn = true;
}
}
else
{
// Row preferred height
final int prev = cps.get ( parent.getComponent ( j - 1 ) ).height;
final int rh = Math.max ( ps.height, prev );
// Second column
final int cy = fillRightSideHeight ? y : getSideY ( false, y, rh, ps.height );
final int ch = fillRightSideHeight ? rh : ps.height;
final int aw = Math.min ( ps.width, rpw );
if ( pos.equals ( LEFT ) )
{
component.setBounds ( x2, cy, aw, ch );
}
else if ( pos.equals ( CENTER ) )
{
component.setBounds ( x2 + rpw / 2 - aw / 2, cy, aw, ch );
}
else if ( pos.equals ( RIGHT ) )
{
component.setBounds ( x2 + rpw - aw, cy, aw, ch );
}
else if ( pos.equals ( FILL ) )
{
component.setBounds ( x2, cy, rpw, ch );
}
// Incrementing Y position
y += rh + verticalGap;
// Going to next line
wasFirstColumn = false;
}
}
}
}
/**
* Returns component Y coordinate for the specified side and other settings.
*
* @param leftSide whether component is on the left side of the form or not
* @param rowY row Y coordinate
* @param rowHeight row height
* @param componentHeight component preferred height
* @return component Y coordinate
*/
protected int getSideY ( final boolean leftSide, final int rowY, final int rowHeight, final int componentHeight )
{
if ( leftSide ? leftVerticalAlignment == SwingConstants.CENTER : rightVerticalAlignment == SwingConstants.CENTER )
{
return rowY + rowHeight / 2 - componentHeight / 2;
}
else if ( leftSide ? leftVerticalAlignment == SwingConstants.TOP : rightVerticalAlignment == SwingConstants.TOP )
{
return rowY;
}
else if ( leftSide ? leftVerticalAlignment == SwingConstants.BOTTOM : rightVerticalAlignment == SwingConstants.BOTTOM )
{
return rowY + rowHeight - componentHeight;
}
return rowY;
}
@Override
public Dimension preferredLayoutSize ( final Container parent )
{
final int cc = parent.getComponentCount ();
final Insets i = parent.getInsets ();
if ( cc > 0 )
{
// Pre-calculating children preferred sizes
final Map cps = SwingUtils.getChildPreferredSizes ( parent );
int pw = 0;
int lpw = 0;
int rpw = 0;
int ph = 0;
boolean wasFirstColumn = false;
for ( int j = 0; j < cc; j++ )
{
final Component thisComponent = parent.getComponent ( j );
final Dimension ps = cps.get ( thisComponent );
if ( !wasFirstColumn )
{
if ( layoutConstraints.get ( thisComponent ).equals ( LINE ) )
{
// Single column line
pw = Math.max ( pw, ps.width );
// Row preferred height
ph += ps.height + verticalGap;
// Skipping to next row
wasFirstColumn = false;
}
else
{
// First column
lpw = Math.max ( lpw, ps.width );
// Row preferred height
final int next = cc > j + 1 ? cps.get ( parent.getComponent ( j + 1 ) ).height : 0;
ph += Math.max ( ps.height, next ) + verticalGap;
// Remembering that this was first column
wasFirstColumn = true;
}
}
else
{
// Second column
rpw = Math.max ( rpw, ps.width );
// Going to next row
wasFirstColumn = false;
}
}
ph -= verticalGap;
if ( fillLeftSide && fillRightSide )
{
lpw = Math.max ( lpw, rpw );
rpw = lpw;
}
return new Dimension ( i.left + Math.max ( lpw + horizontalGap + rpw, pw ) + i.right, i.top + ph + i.bottom );
}
else
{
return new Dimension ( i.left + i.right, i.top + i.bottom );
}
}
}