/*******************************************************************************
* Mission Control Technologies, Copyright (c) 2009-2012, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* The MCT platform is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* MCT includes source code licensed under additional open source licenses. See
* the MCT Open Source Licenses file included with this distribution or the About
* MCT Licenses dialog available at runtime from the MCT Help menu for additional
* information.
*******************************************************************************/
package gov.nasa.arc.mct.gui.util;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JPanel;
/**
* Implements a builder pattern for creating layout constraints for
* the {@link java.awt.GridBagLayout} layout manager.
*/
public class ConstraintBuilder {
/** The default amount of horizontal padding to add to cells. */
public static final int DEFAULT_HPAD = 10;
private Container container;
private GridBagConstraints defaultConstraints;
private GridBagConstraints constraints;
/**
* Creates a new builder with default constraints.
*
* @param container the container
*/
public ConstraintBuilder(Container container) {
this.container = container;
container.setLayout(new GridBagLayout());
constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 0;
constraints.weightx = 0.0;
constraints.weighty = 0.0;
defaultConstraints = (GridBagConstraints) constraints.clone();
}
/**
* Resets all constraints to the default values, and set up for creating
* a single grid cell.
*
* @return the builder
*/
public final ConstraintBuilder reset() {
// Preserve the current location and set up for a single cell.
defaultConstraints.gridx = constraints.gridx;
defaultConstraints.gridy = constraints.gridy;
defaultConstraints.gridheight = 1;
defaultConstraints.gridwidth = 1;
defaultConstraints.weightx = 0.0;
defaultConstraints.weighty = 0.0;
constraints = (GridBagConstraints) defaultConstraints.clone();
return this;
}
/**
* Sets the current constraints to be the defaults.
*
* @return the builder
*/
public final ConstraintBuilder makeDefault() {
defaultConstraints = (GridBagConstraints) constraints.clone();
return this;
}
/**
* Sets the grid position of a component.
*
* @param row the row position for the component
* @param column the column position for the component
* @return the builder
*/
public ConstraintBuilder at(int row, int column) {
constraints.gridy = row;
constraints.gridx = column;
return this;
}
/**
* Sets the grid span of a component.
*
* @param nRows the number of rows the component should span
* @param nColumns the number of columns the component should span
* @return the builder
*/
public ConstraintBuilder span(int nRows, int nColumns) {
constraints.gridheight = nRows;
constraints.gridwidth = nColumns;
return this;
}
/**
* Sets the inset widths for the cell containing the component to add.
*
* @param top padding above of the component
* @param left padding to the left of the component
* @param bottom padding below of the component
* @param right padding to the right of the component
* @return the builder
*/
public ConstraintBuilder insets(int top, int left, int bottom, int right) {
constraints.insets = new Insets(top, left, bottom, right);
return this;
}
/**
* Sets the horizontal padding for the component.
*
* @param padding the amount of padding to add
* @return the builder
*/
public ConstraintBuilder hpad(int padding) {
constraints.ipadx = padding;
return this;
}
/**
* Sets the vertical padding for the component.
*
* @param padding the amount of padding to add
* @return the builder
*/
public ConstraintBuilder vpad(int padding) {
constraints.ipady = padding;
return this;
}
/**
* Sets the anchor position so that the component will be left-aligned
* in its cell, with its baseline aligned with the baselines of
* other components in the same row.
*
* @return the builder
*/
public ConstraintBuilder baseline_w() {
constraints.anchor = GridBagConstraints.BASELINE_LEADING;
return this;
}
/**
* Sets the anchor position so that the component will be centered
* in its cell, with its baseline aligned with the baselines of
* other components in the same row.
*
* @return the builder
*/
public ConstraintBuilder baseline_centered() {
constraints.anchor = GridBagConstraints.BASELINE;
return this;
}
/**
* Sets the anchor position so that the component will be right-aligned
* in its cell, with its baseline aligned with the baselines of
* other components in the same row.
*
* @return the builder
*/
public ConstraintBuilder baseline_e() {
constraints.anchor = GridBagConstraints.BASELINE_TRAILING;
return this;
}
/**
* Sets the anchor position so that the component will be in the
* northwest corner of its cell.
*
* @return the builder
*/
public ConstraintBuilder nw() {
constraints.anchor = GridBagConstraints.NORTHWEST;
return this;
}
/**
* Sets the anchor position so that the component will be in
* the southwest corner of its cell.
*
* @return the builder
*/
public ConstraintBuilder sw() {
constraints.anchor = GridBagConstraints.SOUTHWEST;
return this;
}
/**
* Sets the anchor position so that the component will be centered
* vertically within its cell, at the west edge.
*
* @return the builder
*/
public ConstraintBuilder w() {
constraints.anchor = GridBagConstraints.WEST;
return this;
}
/**
* Sets the constraints so that the component will be sized to
* fill its cell horizontally, if there is extra horizontal room
* in the cell.
*
* @return the builder
*/
public ConstraintBuilder hfill() {
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1.0;
return this;
}
/**
* Sets the constraints so that the component will be sized to
* fill its cell vertically, if there is extra vertical room
* in the cell.
*
* @return the builder
*/
public ConstraintBuilder vfill() {
constraints.fill = GridBagConstraints.VERTICAL;
constraints.weighty = 1.0;
return this;
}
/**
* Sets the constraints so that the component will be sized to
* fill its cell both horizontally and vertically, if there is
* extra room in the cell.
*
* @return the builder
*/
public ConstraintBuilder hvfill() {
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1.0;
constraints.weighty = 1.0;
return this;
}
/**
* Sets the stretch weights so the component will take some of the
* extra space allowed for the container, if any. If the weights
* are set to 0.0, then the component's cell will not expand.
*
* @param weightx the horizontal stretch weight
* @param weighty the vertical stretch weight
* @return the builder
*/
public ConstraintBuilder weight(float weightx, float weighty) {
constraints.weightx = weightx;
constraints.weighty = weighty;
return this;
}
/**
* Gets a clone of the current constraints.
*
* @return a copy of the current constraints
*/
GridBagConstraints getConstraints() {
return (GridBagConstraints) constraints.clone();
}
/**
* Finishes constraint building and adds a component to the container
* using the resulting constraints. Also resets the span to 1x1 and
* the fill to none for the next constraints constructed.
*
* @param component the component to add
*/
public void add(Component component) {
container.add(component, constraints);
reset();
++constraints.gridx;
}
/**
* Moves to the next row down, in the first column.
*
* @return the builder
*/
public ConstraintBuilder nextRow() {
++constraints.gridy;
constraints.gridx = 0;
return this;
}
/**
* Moves to the next column in the grid.
*
* @return the builder
*/
public ConstraintBuilder nextColumn() {
++constraints.gridx;
return this;
}
/**
* Finishes constraint building and adds a sequence of components to the
* container using the resulting constraints. Also resets the span to 1x1 and
* the fill to none for the next constraints constructed. The sequence of
* components is laid out in a subpanel using a flow layout to get left-to-right
* rendering
*
* @param components the component to add
*/
public void add(Component... components) {
JPanel panel = new JPanel();
ConstraintBuilder subBuilder = new ConstraintBuilder(panel);
subBuilder.constraints.anchor = constraints.anchor;
for (Component component : components) {
subBuilder.add(component);
}
add(panel);
}
/**
* Gets a rigid, zero-height component of a desired width, to aid spacing
* apart adjacent components.
*
* @param width the width of the new component
* @return the rigid spacing component
*/
public static Component hbox(int width) {
return hbox(width,width,width);
}
/**
* Returns a stretchable, zero-height component of desired minimum, preferred, and maximum
* width, to aid in spacing apart adjacent components. If all three widths are equal, then
* the spacer will be of rigid width.
*
* @param minWidth the minimum width of the spacer component
* @param preferredWidth the preferred width of the spacer component
* @param maxWidth the maximum width of the spacer comopnent
* @return the stretchable spacer component
*/
public static Component hbox(int minWidth, int preferredWidth, int maxWidth) {
JComponent c = new Box.Filler(new Dimension(minWidth,0), new Dimension(preferredWidth,0), new Dimension(maxWidth,0));
c.setOpaque(false);
return c;
}
}