Implemented a group layout manager that works like the One True Layout

System (as conceived in the Magic User Interface library [MUI] on the
Amiga so long ago). Component weights are not yet supported, but fixedness
works which is mainly what I need.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@23 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2000-12-07 05:41:07 +00:00
parent 0b9e558b4d
commit e621f669b0
6 changed files with 676 additions and 0 deletions
@@ -0,0 +1,44 @@
//
// $Id: DimenInfo.java,v 1.1 2000/12/07 05:41:07 mdb Exp $
package com.samskivert.swing;
import java.awt.Dimension;
/**
* This record is used by the group layout managers to return a set of
* statistics computed for their target widgets.
*/
public class DimenInfo
{
public int count;
public int totwid;
public int tothei;
public int maxwid;
public int maxhei;
public int numfix;
public int fixwid;
public int fixhei;
public int totweight;
public Dimension[] dimens;
public String toString ()
{
StringBuffer buf = new StringBuffer();
buf.append("[count=").append(count);
buf.append(", totwid=").append(totwid);
buf.append(", tothei=").append(tothei);
buf.append(", maxwid=").append(maxwid);
buf.append(", maxhei=").append(maxhei);
buf.append(", numfix=").append(numfix);
buf.append(", fixwid=").append(fixwid);
buf.append(", fixhei=").append(fixhei);
buf.append(", totweight=").append(totweight);
return buf.append("]").toString();
}
}
@@ -0,0 +1,281 @@
//
// $Id: GroupLayout.java,v 1.1 2000/12/07 05:41:07 mdb Exp $
package com.samskivert.swing;
import java.awt.*;
import javax.swing.SwingConstants;
import java.util.HashMap;
/**
* Group layout managers lay out widgets in horizontal or vertical groups.
*/
public abstract class GroupLayout
implements LayoutManager2, SwingConstants
{
/**
* The group layout managers supports two constraints: fixedness
* and weight. A fixed component will not be stretched along the major
* axis of the group. Those components that are stretched will have
* the extra space divided among them according to their weight
* (specifically receiving the ratio of their weight to the total
* weight of all of the free components in the container).
*
* <p/> If a constraints object is constructed with fixedness set to
* true and with a weight, the weight will be ignored.
*/
public static class Constraints
{
/** Whether or not this component is fixed. */
public boolean fixed = false;
/**
* The weight of this component relative to the other components
* in the container.
*/
public int weight = 1;
/**
* Constructs a new constraints object with the specified
* fixedness and weight.
*/
public Constraints (boolean fixed)
{
this.fixed = fixed;
}
/**
* Constructs a new constraints object with the specified
* fixedness and weight.
*/
public Constraints (int weight)
{
this.weight = weight;
}
}
/**
* A constraints object that indicates that the component should be
* fixed and have the default weight of one. This is so commonly used
* that we create and make this object available here.
*/
public final static Constraints FIXED = new Constraints(true);
/**
* Do not adjust the widgets on this axis.
*/
public final static int NONE = 0;
/**
* Stretch all the widgets to their maximum possible size on this
* axis.
*/
public final static int STRETCH = 1;
/**
* Stretch all the widgets to be equal to the size of the largest
* widget on this axis.
*/
public final static int EQUALIZE = 2;
public void setPolicy (int policy)
{
_policy = policy;
}
public int getPolicy ()
{
return _policy;
}
public void setOffAxisPolicy (int offpolicy)
{
_offpolicy = offpolicy;
}
public int getOffAxisPolicy ()
{
return _offpolicy;
}
public void setGap (int gap)
{
_gap = gap;
}
public int getGap ()
{
return _gap;
}
public void setJustification (int justification)
{
_justification = justification;
}
public int getJustification ()
{
return _justification;
}
public void addLayoutComponent (String name, Component comp)
{
// nothing to do here
}
public void removeLayoutComponent (Component comp)
{
if (_constraints != null) {
_constraints.remove(comp);
}
}
public void addLayoutComponent (Component comp, Object constraints)
{
if (constraints != null) {
if (constraints instanceof Constraints) {
if (_constraints == null) {
_constraints = new HashMap();
}
_constraints.put(comp, constraints);
} else {
throw new RuntimeException("GroupLayout constraints " +
"object must be of type " +
"GroupLayout.Constraints");
}
}
}
public float getLayoutAlignmentX (Container target)
{
// we don't support alignment like this
return 0f;
}
public float getLayoutAlignmentY (Container target)
{
// we don't support alignment like this
return 0f;
}
public Dimension minimumLayoutSize (Container parent)
{
return getLayoutSize(parent, MINIMUM);
}
public Dimension preferredLayoutSize (Container parent)
{
return getLayoutSize(parent, PREFERRED);
}
public Dimension maximumLayoutSize (Container parent)
{
return getLayoutSize(parent, MAXIMUM);
}
protected abstract Dimension getLayoutSize (Container parent, int type);
public abstract void layoutContainer (Container parent);
public void invalidateLayout (Container target)
{
// nothing to do here
}
protected boolean isFixed (Component child)
{
if (_constraints == null) {
return false;
}
Constraints c = (Constraints)_constraints.get(child);
if (c != null) {
return c.fixed;
}
return false;
}
protected int getWeight (Component child)
{
if (_constraints == null) {
return 1;
}
Constraints c = (Constraints)_constraints.get(child);
if (c != null) {
return c.weight;
}
return 1;
}
/**
* Computes dimensions of the children widgets that are useful for the
* group layout managers.
*/
protected DimenInfo computeDimens (Container parent, int type)
{
int count = parent.getComponentCount();
DimenInfo info = new DimenInfo();
info.dimens = new Dimension[count];
for (int i = 0; i < count; i++) {
Component child = parent.getComponent(i);
if (!child.isVisible()) {
continue;
}
Dimension csize;
switch (type) {
case MINIMUM:
csize = child.getMinimumSize();
break;
case MAXIMUM:
csize = child.getMaximumSize();
break;
default:
csize = child.getPreferredSize();
break;
}
info.count++;
info.totwid += csize.width;
info.tothei += csize.height;
if (csize.width > info.maxwid) {
info.maxwid = csize.width;
}
if (csize.height > info.maxhei) {
info.maxhei = csize.height;
}
if (isFixed(child)) {
info.fixwid += csize.width;
info.fixhei += csize.height;
info.numfix++;
} else {
info.totweight += getWeight(child);
}
info.dimens[i] = csize;
}
return info;
}
protected int _policy = NONE;
protected int _offpolicy = NONE;
protected int _gap = 5;
protected int _justification = CENTER;
protected HashMap _constraints;
protected static final int MINIMUM = 0;
protected static final int PREFERRED = 1;
protected static final int MAXIMUM = 2;
}
@@ -0,0 +1,40 @@
//
// $Id: GroupLayoutTest.java,v 1.1 2000/12/07 05:41:07 mdb Exp $
package com.samskivert.swing;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GroupLayoutTest
{
public static void main (String[] args)
{
JFrame frame = new JFrame("GroupLayoutTest");
JPanel panel = new JPanel();
GroupLayout layout = new HGroupLayout();
layout.setJustification(GroupLayout.CENTER);
layout.setPolicy(GroupLayout.STRETCH);
layout.setGap(15);
panel.setLayout(layout);
JButton butone = new JButton("One");
panel.add(butone, GroupLayout.FIXED);
JButton buttwo = new JButton("Two");
panel.add(buttwo, GroupLayout.FIXED);
JButton butthree = new JButton("Three to get ready");
panel.add(butthree, GroupLayout.FIXED);
frame.addWindowListener(new WindowAdapter ()
{
public void windowClosing (WindowEvent e)
{
System.exit(0);
}
});
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
@@ -0,0 +1,149 @@
//
// $Id: HGroupLayout.java,v 1.1 2000/12/07 05:41:07 mdb Exp $
package com.samskivert.swing;
import java.awt.*;
public class HGroupLayout extends GroupLayout
{
public HGroupLayout (int policy, int offpolicy, int gap,
int justification)
{
_policy = policy;
_offpolicy = offpolicy;
_gap = gap;
_justification = justification;
}
public HGroupLayout (int policy, int gap, int justification)
{
_policy = policy;
_gap = gap;
_justification = justification;
}
public HGroupLayout (int policy, int justification)
{
_policy = policy;
_justification = justification;
}
public HGroupLayout (int policy)
{
_policy = policy;
}
public HGroupLayout ()
{
}
protected Dimension getLayoutSize (Container parent, int type)
{
DimenInfo info = computeDimens(parent, type);
Dimension dims = new Dimension();
switch (_policy) {
case STRETCH:
case EQUALIZE:
dims.width = info.maxwid * (info.count - info.numfix) +
info.fixwid + _gap * info.count;
break;
case NONE:
default:
dims.width = info.totwid + _gap * info.count;
break;
}
dims.width -= _gap;
dims.height = info.maxhei;
return dims;
}
public void layoutContainer (Container parent)
{
Rectangle b = parent.bounds();
DimenInfo info = computeDimens(parent, PREFERRED);
int nk = parent.getComponentCount();
int sx = 0, sy = 0;
int totwid, totgap = _gap * (info.count-1);
int freecount = info.count - info.numfix;
// do the on-axis policy calculations
int defwid = 0;
switch (_policy) {
case STRETCH:
if (freecount > 0) {
defwid = (b.width - info.fixwid - totgap) / freecount;
totwid = b.width;
} else {
totwid = info.fixwid + totgap;
}
break;
case EQUALIZE:
defwid = info.maxwid;
totwid = info.fixwid + defwid * freecount + totgap;
break;
default:
case NONE:
totwid = info.totwid + totgap;
break;
}
// do the off-axis policy calculations
int defhei = 0;
switch (_offpolicy) {
case STRETCH:
defhei = b.height;
break;
case EQUALIZE:
sy = (b.height - info.maxhei)/2;
defhei = info.maxhei;
break;
default:
case NONE:
break;
}
// do the justification-related calculations
switch (_justification) {
case CENTER:
sx = (b.width - totwid)/2;
break;
case RIGHT:
sx = b.width - totwid;
break;
}
// do the layout
for (int i = 0; i < nk; i++) {
// skip non-visible kids
if (info.dimens[i] == null) {
continue;
}
Component child = parent.getComponent(i);
int newwid = defwid;
int newhei = defhei;
if (_policy == NONE || isFixed(child)) {
newwid = info.dimens[i].width;
}
if (_offpolicy == NONE) {
newhei = info.dimens[i].height;
sy = (b.height - newhei)/2;
}
child.setBounds(sx, sy, newwid, newhei);
sx += child.size().width + _gap;
}
}
}
@@ -0,0 +1,13 @@
#
# $Id: Makefile,v 1.1 2000/12/07 05:41:07 mdb Exp $
ROOT = ../../..
SRCS = \
DimenInfo.java \
GroupLayout.java \
GroupLayoutTest.java \
HGroupLayout.java \
VGroupLayout.java \
include $(ROOT)/build/Makefile.java
@@ -0,0 +1,149 @@
//
// $Id: VGroupLayout.java,v 1.1 2000/12/07 05:41:07 mdb Exp $
package com.samskivert.swing;
import java.awt.*;
public class VGroupLayout extends GroupLayout
{
public VGroupLayout (int policy, int offpolicy, int gap,
int justification)
{
_policy = policy;
_offpolicy = offpolicy;
_gap = gap;
_justification = justification;
}
public VGroupLayout (int policy, int gap, int justification)
{
_policy = policy;
_gap = gap;
_justification = justification;
}
public VGroupLayout (int policy, int justification)
{
_policy = policy;
_justification = justification;
}
public VGroupLayout (int policy)
{
_policy = policy;
}
public VGroupLayout ()
{
}
protected Dimension getLayoutSize (Container parent, int type)
{
DimenInfo info = computeDimens(parent, type);
Dimension dims = new Dimension();
switch (_policy) {
case STRETCH:
case EQUALIZE:
dims.height = info.maxhei * (info.count - info.numfix) +
info.fixhei + _gap * info.count;
break;
case NONE:
default:
dims.height = info.tothei + _gap * info.count;
break;
}
dims.height -= _gap;
dims.width = info.maxwid;
return dims;
}
public void layoutContainer (Container parent)
{
Rectangle b = parent.bounds();
DimenInfo info = computeDimens(parent, PREFERRED);
int nk = parent.getComponentCount();
int sx = 0, sy = 0;
int tothei, totgap = _gap * (info.count-1);
int freecount = info.count - info.numfix;
// do the on-axis policy calculations
int defhei = 0;
switch (_policy) {
case STRETCH:
if (freecount > 0) {
defhei = (b.height - info.fixhei - totgap) / freecount;
tothei = b.height;
} else {
tothei = info.fixhei + totgap;
}
break;
case EQUALIZE:
defhei = info.maxhei;
tothei = info.fixhei + defhei * freecount + totgap;
break;
default:
case NONE:
tothei = info.tothei + totgap;
break;
}
// do the off-axis policy calculations
int defwid = 0;
switch (_offpolicy) {
case STRETCH:
defwid = b.width;
break;
case EQUALIZE:
sx = (b.width - info.maxwid)/2;
defwid = info.maxwid;
break;
default:
case NONE:
break;
}
// do the justification-related calculations
switch (_justification) {
case CENTER:
sy = (b.height - tothei)/2;
break;
case RIGHT:
sy = b.height - tothei;
break;
}
// do the layout
for (int i = 0; i < nk; i++) {
// skip non-visible kids
if (info.dimens[i] == null) {
continue;
}
Component child = parent.getComponent(i);
int newhei = defhei;
int newwid = defwid;
if (_policy == NONE || isFixed(child)) {
newhei = info.dimens[i].height;
}
if (_offpolicy == NONE) {
newwid = info.dimens[i].width;
sx = (b.width - newwid)/2;
}
child.setBounds(sx, sy, newwid, newhei);
sy += child.size().height + _gap;
}
}
}