org.jdesktop.layout.AquaLayoutStyle Maven / Gradle / Ivy
Show all versions of swing-layout Show documentation
/*
* Copyright (C) 2005-2006 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.layout;
import javax.swing.border.EmptyBorder;
import org.jdesktop.layout.*;
import java.awt.*;
import java.lang.reflect.*;
import javax.swing.*;
import java.util.*;
/**
* An implementation of LayoutStyle
for Mac OS X Tiger.
*
* The information used for this layout style comes from:
* http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/
*
* @author Werner Randelshofer
* @version $Revision: 1.3 $
*/
class AquaLayoutStyle extends LayoutStyle {
private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);
/** Mini size style. */
private final static int MINI = 0;
/** Small size style. */
private final static int SMALL = 1;
/** Regular size style. */
private final static int REGULAR = 2;
/**
* The containerGapDefinitions array defines the preferred insets (child gaps)
* of a parent container towards one of its child components.
*
* Note: As of now, we do not yet specify the preferred gap from a child
* to its parent. Therefore we may not be able to treat all special cases.
*
* This array is used to initialize the containerGaps HashMap.
*
* The array has the following structure, which is supposed to be a
* a compromise between legibility and code size.
* containerGapDefinitions[0..n] = preferred insets for some parent UI's
* containerGapDefinitions[][0..m-3] = name of parent UI,
* optionally followed by a full stop and
* a style name
* containerGapDefinitions[][m-2] = mini insets
* containerGapDefinitions[][m-1] = small insets
* containerGapDefinitions[][m] = regular insets
*/
private final static Object[][] containerGapDefinitions = {
// Format:
// { list of parent UI's,
// mini insets, small insets, regular insets }
{ "TabbedPaneUI",
new Insets(6,10,10,10), new Insets(6,10,10,12),
new Insets(12,20,20,20)
},
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_3.html#//apple_ref/doc/uid/TP30000360/DontLinkElementID_27
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_3.html#//apple_ref/doc/uid/TP30000360/DontLinkElementID_26
// note for small and mini size: leave 8 to 10 pixels on top
// note for regular size: leave only 12 pixel at top if tabbed pane UI
{ "RootPaneUI",
new Insets(8,10,10,10), new Insets(8,10,10,12),
new Insets(14,20,20,20)
},
// These child gaps are used for all other components
{ "default",
new Insets(8,10,10,10), new Insets(8,10,10,12),
new Insets(14,20,20,20)
},
};
/**
* The relatedGapDefinitions table defines the preferred gaps
* of one party of two related components.
*
* The effective preferred gap is the maximum of the preferred gaps of
* both parties.
*
* This array is used to initialize the relatedGaps HashMap.
*
* The array has the following structure, which is supposed to be a
* a compromise between legibility and code size.
* containerGapDefinitions[0..n] = preferred gaps for a party of a two related UI's
* containerGapDefinitions[][0..m-3] = name of UI
* optionally followed by a full stop and
* a style name
* containerGapDefinitions[][m-2] = mini insets
* containerGapDefinitions[][m-1] = small insets
* containerGapDefinitions[][m] = regular insets
*/
private final static Object[][] relatedGapDefinitions = {
// Format:
// { list of UI's,
// mini insets, small insets, regular insets }
// Push Button:
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF104
{ "ButtonUI", "ButtonUI.push", "ButtonUI.text",
"ToggleButtonUI.push", "ToggleButtonUI.text",
new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12)
},
// Metal Button
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF187
{ "ButtonUI.metal", "ToggleButtonUI.metal",
new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(12,12,12,12)
},
// Bevel Button (Rounded and Square)
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF112
{ "ButtonUI.bevel", "ButtonUI.toggle", "ButtonUI.square",
"ToggleButtonUI", "ToggleButtonUI.bevel", "ToggleButtonUI.square",
"ToggleButtonUI.toggle",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
// Bevel Button (Rounded and Square)
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF112
{ "ButtonUI.bevel.largeIcon", "ToggleButtonUI.bevel.largeIcon",
new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(8,8,8,8)
},
// Icon Button
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF189
{ "ButtonUI.icon",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
{ "ButtonUI.icon.largeIcon",
new Insets(8,8,8,8), new Insets(8,8,8,8), new Insets(8,8,8,8)
},
// Round Button
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF191
{ "ButtonUI.round", "ToggleButtonUI.round",
new Insets(12,12,12,12), new Insets(12,12,12,12),
new Insets(12,12,12,12)
},
// Help Button
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF193
{ "ButtonUI.help",
new Insets(12,12,12,12), new Insets(12,12,12,12),
new Insets(12,12,12,12)
},
// Segmented Control
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF196
{ "ButtonUI.toggleCenter", "ToggleButtonUI.toggleCenter",
new Insets(8,0,8,0), new Insets(10,0,10,0), new Insets(12,0,12,0)
},
{ "ButtonUI.toggleEast", "ToggleButtonUI.toggleEast",
new Insets(8,0,8,8), new Insets(10,0,10,10), new Insets(12,0,12,12)
},
{ "ButtonUI.toggleWest", "ToggleButtonUI.toggleWest",
new Insets(8,8,8,0), new Insets(10,10,10,0), new Insets(12,12,12,0)
},
{ "ButtonUI.toolBarTab", "ToggleButtonUI.toolBarTab",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
// Color Well Button
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF213
{ "ButtonUI.colorWell", "ToggleButtonUI.colorWell",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF198
// FIXME - The following values are given in the AHIG.
// In reality, the values further below seem to be more appropriate.
// Which ones are right?
//{ "CheckBoxUI", new Insets(7, 5, 7, 5), new Insets(8, 6, 8, 6), new Insets(8, 8, 8, 8) },
{ "CheckBoxUI",
new Insets(6, 5, 6, 5), new Insets(7, 6, 7, 6), new Insets(7, 6, 7, 6)
},
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF198
{ "ComboBoxUI.editable",
new Insets(8, 5, 8, 5), new Insets(10, 6, 10, 6),
new Insets(12, 8, 12, 8)
},
{ "ComboBoxUI.uneditable",
new Insets(6, 5, 6, 5), new Insets(8, 6, 8, 6),
new Insets(10, 8, 10, 8)
},
// There is no spacing given for labels.
// This comes from playing with IB.
// We use the values here, which is the minimum of the spacing of all
// other components.
{ "LabelUI",
new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8), new Insets(8, 8, 8, 8)
},
// ? spacing not given
{ "ListUI",
new Insets(5, 5, 5, 5), new Insets(6, 6, 6, 6), new Insets(6, 6, 6, 6)
},
// ? spacing not given
{ "PanelUI",
new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0), new Insets(0, 0, 0, 0)
},
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_5.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF106
// ? spacing not given
{ "ProgressBarUI",
new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12)
},
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_3.html#//apple_ref/doc/uid/20000957-TP30000359-BIAHBFAD
{ "RadioButtonUI",
new Insets(5, 5, 5, 5), new Insets(6, 6, 6, 6), new Insets(6, 6, 6, 6)
},
//http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_6.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF114
// ? spacing not given. We use the same as for text fields.
{ "ScrollPaneUI",
new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8),
new Insets(8, 10, 8, 10)
},
//http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_8.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF214
// ? spacing not given
//http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGLayout/chapter_19_section_2.html#//apple_ref/doc/uid/20000957-TP30000360-CHDEACGD
{ "SeparatorUI",
new Insets(8, 8, 8, 8), new Insets(10, 10, 10, 10),
new Insets(12, 12, 12, 12)
},
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_4.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF115
{ "SliderUI.horizontal",
new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12)
},
{ "SliderUI.vertical",
new Insets(8,8,8,8), new Insets(10,10,10,10), new Insets(12,12,12,12)
},
//http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_4.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF204
{ "SpinnerUI",
new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8),
new Insets(8, 10, 8, 10)
},
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_7.html#//apple_ref/doc/uid/20000957-TP30000359-CHDDBIJE
// ? spacing not given
{ "SplitPaneUI",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_7.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF105
// ? spacing not given
{ "TabbedPaneUI",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
{ "TableUI",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
// ? spacing not given
{ "TextAreaUI", "EditorPaneUI", "TextPaneUI",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
//http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_6.html#//apple_ref/doc/uid/20000957-TP30000359-TPXREF225
{ "TextFieldUI", "FormattedTextFieldUI", "PasswordFieldUI",
new Insets(6, 8, 6, 8), new Insets(6, 8, 6, 8),
new Insets(8, 10, 8, 10)
},
// ? spacing not given
{ "TreeUI",
new Insets(0,0,0,0), new Insets(0,0,0,0), new Insets(0,0,0,0)
},
};
private final static Object[][] unrelatedGapDefinitions = {
// UI, mini, small, regular
{ "ButtonUI.help",
new Insets(24,24,24,24), new Insets(24,24,24,24),
new Insets(24,24,24,24)
},
{ "default",
new Insets(10, 10, 10, 10), new Insets(12, 12, 12, 12),
new Insets(14, 14, 14, 14)
},
};
/**
* The indentGapDefinitions table defines the preferred indentation
* for components that are indented after the specified component.
*
* This array is used to initialize the indentGaps HashMap.
*
* The array has the following structure, which is supposed to be a
* a compromise between legibility and code size.
* indentGapDefinitions[0..n] = preferred gaps for a party of a two related UI's
* indentGapDefinitions[][0..m-3] = name of UI
* optionally followed by a full stop and
* a style name
* indentGapDefinitions[][m-2] = mini insets
* indentGapDefinitions[][m-1] = small insets
* indentGapDefinitions[][m] = regular insets
*/
private final static Object[][] indentGapDefinitions = {
// UI, mini, small, regular
// The Aqua L&F does not scale button images of check boxes and radio
// buttons. Therefore we use to the same horizontal indents for all sizes.
{ "CheckBoxUI", "RadioButtonUI",
new Insets(16, 24, 16, 24), new Insets(20, 24, 20, 24),
new Insets(24, 24, 24, 24) },
{ "default",
new Insets(16, 16, 16, 16), new Insets(20, 20, 20, 20),
new Insets(24, 24, 24, 24) },
};
/**
* The visualMarginDefinition table defines the visually perceived
* margin of the components.
*
* This array is used to initialize the visualMargins HashMap.
*
* The array has the following structure, which is supposed to be a
* a compromise between legibility and code size.
* visualMarginDefinitions[0..n] = preferred gaps for a party of a two related UI's
* visualMarginDefinitions[][0..m-1] = name of UI
* optionally followed by a full stop and
* a style name
* containerGapDefinitions[][m] = visual margins
*/
private final static Object[][] visualMarginDefinitions = {
// UI, regular
{ "ButtonUI", "ButtonUI.text",
"ToggleButtonUI", "ToggleButtonUI.text",
new Insets(5, 3, 3, 3)
},
{ "ButtonUI.icon",
"ToggleButtonUI.icon",
new Insets(5, 2, 3, 2)
},
{ "ButtonUI.toolbar",
"ToggleButtonUI.toolbar",
new Insets(0, 0, 0, 0)
},
{ "CheckBoxUI", new Insets(4, 4, 3, 3) },
{ "ComboBoxUI", new Insets(2, 3, 4, 3) },
{ "DesktopPaneUI", new Insets(0, 0, 0, 0) },
{ "EditorPaneUI", "TextAreaUI", "TextPaneUI",
new Insets(0, 0, 0, 0)
},
{ "FormattedTextFieldUI", "PasswordFieldUI", "TextFieldUI",
new Insets(0, 0, 0, 0)
},
{ "LabelUI", new Insets(0, 0, 0, 0) },
{ "ListUI", new Insets(0, 0, 0, 0) },
{ "PanelUI", new Insets(0, 0, 0, 0) },
{ "ProgressBarUI", "ProgressBarUI.horizontal", new Insets(0, 2, 4, 2) },
{ "ProgressBarUI.vertical", new Insets(2, 0, 2, 4) },
{ "RadioButtonUI", new Insets(4, 4, 3, 3) },
{ "ScrollBarUI", new Insets(0, 0, 0, 0) },
{ "ScrollPaneUI", new Insets(0, 0, 0, 0) },
{ "SpinnerUI", new Insets(0, 0, 0, 0) },
{ "SeparatorUI", new Insets(0, 0, 0, 0) },
{ "SplitPaneUI", new Insets(0, 0, 0, 0) },
{ "SliderUI", "SliderUI.horizontal", new Insets(3, 6, 3, 6) },
{ "SliderUI.vertical", new Insets(6, 3, 6, 3) },
{ "TabbedPaneUI", "TabbedPaneUI.top", new Insets(5, 7, 10, 7) },
{ "TabbedPaneUI.bottom", new Insets(4, 7, 5, 7) },
{ "TabbedPaneUI.left", new Insets(4, 6, 10, 7) },
{ "TabbedPaneUI.right", new Insets(4, 7, 10, 6) },
{ "TableUI", new Insets(0, 0, 0, 0) },
{ "TreeUI", new Insets(0, 0, 0, 0) },
{ "default", new Insets(0, 0, 0, 0) },
};
/**
* The relatedGaps map defines the preferred gaps
* of one party of two related components.
*/
private final static Map RELATED_GAPS = createInsetsMap(relatedGapDefinitions);
/**
* The unrelatedGaps map defines the preferred gaps
* of one party of two unrelated components.
*/
private final static Map UNRELATED_GAPS = createInsetsMap(unrelatedGapDefinitions);
/**
* The containerGaps map defines the preferred insets (child gaps)
* of a parent component towards one of its children.
*/
private final static Map CONTAINER_GAPS = createInsetsMap(containerGapDefinitions);
/**
* The indentGaps map defines the preferred indentation
* for components that are indented after the specified component.
*/
private final static Map INDENT_GAPS = createInsetsMap(indentGapDefinitions);
/**
* The visualMargins map defines the preferred indentation
* for components that are indented after the specified component.
*/
private final static Map VISUAL_MARGINS = createInsetsMap(visualMarginDefinitions);
/**
* Creates a map for the specified definitions array.
*
* The key for the map is the name of the UI, for example, ButtonUI, with
* a value of ComponentInsets. Each ComponentInsets may have sub styles.
*/
// private static Map createInsetsMap(Object[][] definitions) {
private static Map createInsetsMap(Object[][] definitions) {
Map map = new HashMap();
for (int i=0; i < definitions.length; i++) {
int keys = 0;
while (keys < definitions[i].length &&
(definitions[i][keys] instanceof String)) {
keys++;
}
Insets[] values = new Insets[definitions[i].length - keys];
for (int j=keys; j < definitions[i].length; j++) {
values[j-keys] = (Insets) definitions[i][j];
}
for (int j=0; j < keys; j++) {
String key = (String)definitions[i][j];
int subindex = key.indexOf('.');
if (subindex == -1) {
ComponentInsets componentInsets = (ComponentInsets)map.get(key);
if (componentInsets == null) {
componentInsets = new ComponentInsets(values);
map.put(key, new ComponentInsets(values));
} else {
assert (componentInsets.getInsets() == null);
componentInsets.setInsets(values);
}
} else {
String subkey = key.substring(subindex + 1);
String parentKey = key.substring(0, subindex);
ComponentInsets componentInsets = (ComponentInsets)
map.get(parentKey);
if (componentInsets == null) {
componentInsets = new ComponentInsets();
map.put(parentKey, componentInsets);
}
componentInsets.addSubinsets(subkey,
new ComponentInsets(values));
}
}
}
return map;
}
public static void main(String[] args) {
JButton button = new JButton();
button.putClientProperty("JButton.buttonType", "metal");
JButton button2 = new JButton();
LayoutStyle style = new AquaLayoutStyle();
int gap =
style.getPreferredGap(button, button2, LayoutStyle.RELATED, SwingConstants.EAST,
null);
System.err.println("gap= " + gap);
button.putClientProperty("JButton.buttonType", "square");
button2.putClientProperty("JButton.buttonType", "square");
gap =
style.getPreferredGap(button, button2, LayoutStyle.RELATED, SwingConstants.EAST,
null);
System.err.println("gap= " + gap);
}
/**
* Creates a new instance.
*/
public AquaLayoutStyle() {
}
/**
* Returns the amount of space to use between two components.
* The return value indicates the distance to place
* component2
relative to component1
.
* For example, the following returns the amount of space to place
* between component2
and component1
* when component2
is placed vertically above
* component1
:
*
* int gap = getPreferredGap(component1, component2,
* LayoutStyle.RELATED,
* SwingConstants.NORTH, parent);
*
* The type
parameter indicates the type
* of gap being requested. It can be one of the following values:
*
* RELATED
* If the two components will be contained in
* the same parent and are showing similar logically related
* items, use RELATED
.
* UNRELATED
* If the two components will be
* contained in the same parent but show logically unrelated items
* use UNRELATED
.
* INDENT
* Used to obtain the preferred distance to indent a component
* relative to another. For example, if you want to horizontally
* indent a JCheckBox relative to a JLabel use INDENT
.
* This is only useful for the horizontal axis.
*
*
* It's important to note that some look and feels may not distinguish
* between RELATED
and UNRELATED
.
*
* The return value is not intended to take into account the
* current size and position of component2
or
* component1
. The return value may take into
* consideration various properties of the components. For
* example, the space may vary based on font size, or the preferred
* size of the component.
*
* @param component1 the JComponent
* component2
is being placed relative to
* @param component2 the JComponent
being placed
* @param type how the two components are being placed
* @param position the position component2
is being placed
* relative to component1
; one of
* SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
* @param parent the parent of component2
; this may differ
* from the actual parent and may be null
* @return the amount of space to place between the two components
* @throws IllegalArgumentException if position
is not
* one of SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
; type
not one
* of INDENT
, RELATED
* or UNRELATED
; or component1
or
* component2
is null
*/
public int getPreferredGap(JComponent component1, JComponent component2,
int type, int position, Container parent) {
// Check args
super.getPreferredGap(component1, component2, type, position, parent);
int result;
// Compute gap
if (type == INDENT) {
// Compute gap
if (position == SwingConstants.EAST || position == SwingConstants.WEST) {
int gap = getButtonChildIndent(component1, position);
if (gap != 0) {
return gap;
}
}
int sizeStyle = getSizeStyle(component1);
Insets gap1 = getPreferredGap(component1, type, sizeStyle);
switch (position) {
case SwingConstants.NORTH :
result = gap1.bottom;
break;
case SwingConstants.SOUTH :
result = gap1.top;
break;
case SwingConstants.EAST :
result = gap1.left;
break;
case SwingConstants.WEST :
default :
result = gap1.right;
break;
}
int raw = result;
// Compensate for visual margin
Insets visualMargin2 = getVisualMargin(component2);
switch (position) {
case SwingConstants.NORTH :
result -= visualMargin2.bottom;
break;
case SwingConstants.SOUTH :
result -= visualMargin2.top;
break;
case SwingConstants.EAST :
result -= visualMargin2.left;
break;
case SwingConstants.WEST :
result -= visualMargin2.right;
default :
break;
}
} else {
// Compute gap
int sizeStyle = Math.min(getSizeStyle(component1),
getSizeStyle(component2));
Insets gap1 = getPreferredGap(component1, type, sizeStyle);
Insets gap2 = getPreferredGap(component2, type, sizeStyle);
switch (position) {
case SwingConstants.NORTH :
result = Math.max(gap1.top, gap2.bottom);
break;
case SwingConstants.SOUTH :
result = Math.max(gap1.bottom, gap2.top);
break;
case SwingConstants.EAST :
result = Math.max(gap1.right, gap2.left);
break;
case SwingConstants.WEST :
default :
result = Math.max(gap1.left, gap2.right);
break;
}
// Compensate for visual margin
Insets visualMargin1 = getVisualMargin(component1);
Insets visualMargin2 = getVisualMargin(component2);
switch (position) {
case SwingConstants.NORTH :
result -= visualMargin1.top + visualMargin2.bottom;
break;
case SwingConstants.SOUTH :
result -= visualMargin1.bottom + visualMargin2.top;
break;
case SwingConstants.EAST :
result -= visualMargin1.right + visualMargin2.left;
break;
case SwingConstants.WEST :
result -= visualMargin1.left + visualMargin2.right;
default :
break;
}
}
// Aqua does not support negative gaps, because all its components are
// opaque
return Math.max(0, result);
}
private Insets getPreferredGap(JComponent component, int type, int sizeStyle) {
Map gapMap;
switch (type) {
case INDENT :
gapMap = INDENT_GAPS;
break;
case RELATED :
gapMap = RELATED_GAPS;
break;
case UNRELATED :
default :
gapMap = UNRELATED_GAPS;
break;
}
String uid = component.getUIClassID();
String style = null;
// == is ok here as Strings from Swing get interned, if for some reason
// need .equals then must deal with null.
if (uid == "ButtonUI" || uid =="ToggleButtonUI") {
style = (String) component.getClientProperty("JButton.buttonType");
} else if (uid =="ProgressBarUI") {
style = (((JProgressBar) component).getOrientation() ==
JProgressBar.HORIZONTAL) ? "horizontal" : "vertical";
} else if (uid == "SliderUI") {
style = (((JSlider) component).getOrientation()
== JProgressBar.HORIZONTAL) ? "horizontal" : "vertical";
} else if (uid == "TabbedPaneUI") {
switch (((JTabbedPane) component).getTabPlacement()) {
case JTabbedPane.TOP :
style = "top";
break;
case JTabbedPane.LEFT :
style = "left";
break;
case JTabbedPane.BOTTOM :
style = "bottom";
break;
case JTabbedPane.RIGHT :
style = "right";
break;
}
} else if (uid == "ComboBoxUI") {
style = ((JComboBox) component).isEditable() ? "editable" : "uneditable";
}
return getInsets(gapMap, uid, style, sizeStyle);
}
/**
* Returns the amount of space to position a component inside its
* parent.
*
* @param component the Component
being positioned
* @param position the position component
is being placed
* relative to its parent; one of
* SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
* @param parent the parent of component
; this may differ
* from the actual parent and may be null
* @return the amount of space to place between the component and specified
* edge
* @throws IllegalArgumentException if position
is not
* one of SwingConstants.NORTH
,
* SwingConstants.SOUTH
,
* SwingConstants.EAST
or
* SwingConstants.WEST
;
* or component
is null
*/
public int getContainerGap(JComponent component, int position,
Container parent) {
int result;
int sizeStyle = Math.min(getSizeStyle(component), getSizeStyle(parent));
// Compute gap
Insets gap = getContainerGap(parent, sizeStyle);
switch (position) {
case SwingConstants.NORTH :
result = gap.top;
break;
case SwingConstants.SOUTH :
result = gap.bottom;
break;
case SwingConstants.EAST :
result = gap.right;
break;
case SwingConstants.WEST :
default :
result = gap.left;
break;
}
// Compensate for visual margin
Insets visualMargin = getVisualMargin(component);
switch (position) {
case SwingConstants.NORTH :
result -= visualMargin.top;
break;
case SwingConstants.SOUTH :
result -= visualMargin.bottom;
// Radio buttons in Quaqua are 1 pixel too high, in order
// to align their baselines with other components, when no
// baseline aware layout manager is used.
if (component instanceof JRadioButton) {
result--;
}
break;
case SwingConstants.EAST :
result -= visualMargin.left;
break;
case SwingConstants.WEST :
result -= visualMargin.right;
default :
break;
}
// Aqua does not support negative gaps, because all its components are
// opaque
return Math.max(0, result);
}
private Insets getContainerGap(Container container, int sizeStyle) {
String uid;
if (container instanceof JComponent) {
uid = ((JComponent) container).getUIClassID();
} else if (container instanceof Dialog) {
uid = "Dialog";
} else if (container instanceof Frame) {
uid = "Frame";
} else if (container instanceof java.applet.Applet) {
uid = "Applet";
} else if (container instanceof Panel) {
uid = "Panel";
} else {
uid = "default";
}
// FIXME insert style code here for JInternalFrame with palette style
return getInsets(CONTAINER_GAPS, uid, null, sizeStyle);
}
private Insets getInsets(Map gapMap, String uid, String style,
int sizeStyle) {
if (uid == null) {
uid = "default";
}
ComponentInsets componentInsets = (ComponentInsets)gapMap.get(uid);
if (componentInsets == null) {
componentInsets = (ComponentInsets)gapMap.get("default");
if (componentInsets == null) {
return EMPTY_INSETS;
}
} else if (style != null) {
ComponentInsets subInsets = componentInsets.getSubinsets(style);
if (subInsets != null) {
componentInsets = subInsets;
}
}
return componentInsets.getInsets(sizeStyle);
}
private Insets getVisualMargin(JComponent component) {
String uid = component.getUIClassID();
String style = null;
if (uid == "ButtonUI" || uid == "ToggleButtonUI") {
style = (String) component.getClientProperty("JButton.buttonType");
} else if (uid == "ProgressBarUI") {
style = (((JProgressBar) component).getOrientation()
== JProgressBar.HORIZONTAL) ? "horizontal" :
"vertical";
} else if (uid == "SliderUI") {
style = (((JSlider) component).getOrientation() ==
JProgressBar.HORIZONTAL) ? "horizontal"
: "vertical";
} else if (uid == "TabbedPaneUI") {
switch (((JTabbedPane) component).getTabPlacement()) {
case JTabbedPane.TOP :
style = "top";
break;
case JTabbedPane.LEFT :
style = "left";
break;
case JTabbedPane.BOTTOM :
style = "bottom";
break;
case JTabbedPane.RIGHT :
style = "right";
break;
}
}
Insets gap = getInsets(VISUAL_MARGINS, uid, style, 0);
// Take into account different positions of the button icon
if (uid == "RadioButtonUI" || uid == "CheckBoxUI") {
switch (((AbstractButton) component).getHorizontalTextPosition()) {
case SwingConstants.RIGHT :
gap = new Insets(gap.top, gap.right, gap.bottom, gap.left);
break;
case SwingConstants.CENTER :
gap = new Insets(gap.top, gap.right, gap.bottom, gap.right);
break;
/*
case SwingConstants.LEFT :
break;
*/
default:
gap = new Insets(gap.top, gap.left, gap.bottom, gap.right);
}
if (component.getBorder() instanceof EmptyBorder) {
gap.left -= 2;
gap.right -= 2;
gap.top -= 2;
gap.bottom -= 2;
}
}
return gap;
}
/**
* Returns the size style of a specified component.
*
* @return REGULAR, SMALL or MINI.
*/
private int getSizeStyle(Component c) {
// Aqua components have a different style depending on the
// font size used.
// 13 Point = Regular
// 11 Point = Small
// 9 Point = Mini
if (c == null) {
return REGULAR;
}
Font font = c.getFont();
if (font == null) {
return REGULAR;
}
int fontSize = font.getSize();
return (fontSize >= 13) ? REGULAR : ((fontSize > 9) ? SMALL : MINI);
}
/**
* ComponentInsets is used to manage the Insets for a specific Component
* type. Each ComponentInsets may also have children (sub) ComponentInsets.
* Subinsets are used to represent different styles a component may have.
* For example, a Button may not a set of insets, as well as insets when
* it has a style of metal.
*/
private static class ComponentInsets {
// Map
private Map children;
private Insets[] insets;
public ComponentInsets() {
}
public ComponentInsets(Insets[] insets) {
this.insets = insets;
}
public void setInsets(Insets[] insets) {
this.insets = insets;
}
public Insets[] getInsets() {
return insets;
}
public Insets getInsets(int size) {
if (insets == null) {
return EMPTY_INSETS;
}
return insets[size];
}
void addSubinsets(String subkey, ComponentInsets subinsets) {
if (children == null) {
children = new HashMap(5);
}
children.put(subkey, subinsets);
}
ComponentInsets getSubinsets(String subkey) {
return (children == null) ? null :
(ComponentInsets)children.get(subkey);
}
}
}