org.jdesktop.swingx.plaf.BuddyLayoutAndBorder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swingx Show documentation
Show all versions of swingx Show documentation
Contains extensions to the Swing GUI toolkit, including new and enhanced components that provide functionality commonly required by rich client applications.
The newest version!
package org.jdesktop.swingx.plaf;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicBorders.MarginBorder;
import org.jdesktop.swingx.prompt.BuddySupport;
import org.jdesktop.swingx.prompt.BuddySupport.Position;
public class BuddyLayoutAndBorder implements LayoutManager, Border, PropertyChangeListener, UIResource {
private JTextField textField;
private Border borderDelegate;
/**
* Installs a {@link BuddyLayoutAndBorder} as a layout and border of the
* given text field. Registers a {@link PropertyChangeListener} to wrap any
* subsequently set border on the text field.
*/
protected void install(JTextField textField) {
uninstall();
this.textField = textField;
textField.setLayout(this);
replaceBorderIfNecessary();
textField.addPropertyChangeListener("border", this);
}
public Border getBorderDelegate() {
return borderDelegate;
}
/**
* Wraps and replaces the text fields default border with this object, to
* honor the button margins and sizes of the search, clear and popup buttons
* and the layout style.
*/
protected void replaceBorderIfNecessary() {
Border original = textField.getBorder();
if (!(original instanceof BuddyLayoutAndBorder)) {
borderDelegate = original;
textField.setBorder(this);
}
}
/**
* Does nothing.
*
* @see BuddySupport#add(javax.swing.JComponent, Position, JTextField)
*/
public void addLayoutComponent(String name, Component comp) {
}
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
public Dimension preferredLayoutSize(Container parent) {
Dimension d = new Dimension();
// height of highest buddy.
for (Component c : BuddySupport.getLeft(textField)) {
d.height = Math.max(d.height, c.getPreferredSize().height);
}
for (Component c : BuddySupport.getRight(textField)) {
d.height = Math.max(d.height, c.getPreferredSize().height);
}
Insets insets = getRealBorderInsets();
d.height += insets.top + insets.bottom;
d.width += insets.left + insets.right;
Insets outerMargin = BuddySupport.getOuterMargin(textField);
if (outerMargin != null) {
d.width += outerMargin.left + outerMargin.right;
d.height += outerMargin.bottom + outerMargin.top;
}
return d;
}
/**
* Does nothing.
*
* @see BuddySupport#remove(javax.swing.JComponent, JTextField)
*/
public void removeLayoutComponent(Component comp) {
}
public void layoutContainer(Container parent) {
Rectangle visibleRect = getVisibleRect();
Dimension size;
for (Component comp : BuddySupport.getLeft(textField)) {
if (!comp.isVisible()) {
continue;
}
size = comp.getPreferredSize();
comp.setBounds(visibleRect.x, centerY(visibleRect, size), size.width, size.height);
visibleRect.x += size.width;
visibleRect.width -= size.width;
}
for (Component comp : BuddySupport.getRight(textField)) {
if (!comp.isVisible()) {
continue;
}
size = comp.getPreferredSize();
comp.setBounds(visibleRect.x + visibleRect.width - size.width, centerY(visibleRect, size), size.width,
size.height);
visibleRect.width -= size.width;
}
}
protected int centerY(Rectangle rect, Dimension size) {
return (int) (rect.getCenterY() - (size.height / 2));
}
/**
* @return the rectangle allocated by the text field, including the space
* allocated by the child components left and right, the text fields
* original border insets and the outer margin.
*
*/
protected Rectangle getVisibleRect() {
Rectangle alloc = SwingUtilities.getLocalBounds(textField);
substractInsets(alloc, getRealBorderInsets());
substractInsets(alloc, BuddySupport.getOuterMargin(textField));
return alloc;
}
private void substractInsets(Rectangle alloc, Insets insets) {
if (insets != null) {
alloc.x += insets.left;
alloc.y += insets.top;
alloc.width -= insets.left + insets.right;
alloc.height -= insets.top + insets.bottom;
}
}
/**
* Returns the {@link Insets} of the original {@link Border} plus the space
* required by the child components.
*
* @see javax.swing.border.Border#getBorderInsets(java.awt.Component)
*/
public Insets getBorderInsets(Component c) {
Insets insets = null;
if (borderDelegate != null) {
// Original insets are cloned to make it work in Mac OS X Aqua LnF.
// Seems that this LnF uses a shared insets instance which should
// not be modified.
// Include margin here
insets = (Insets) borderDelegate.getBorderInsets(textField).clone();
} else {
insets = new Insets(0, 0, 0, 0);
}
//somehow this happens sometimes
if (textField == null) {
return insets;
}
for (Component comp : BuddySupport.getLeft(textField)) {
insets.left += comp.isVisible() ? comp.getPreferredSize().width : 0;
}
for (Component comp : BuddySupport.getRight(textField)) {
insets.right += comp.isVisible() ? comp.getPreferredSize().width : 0;
}
Insets outerMargin = BuddySupport.getOuterMargin(textField);
if (outerMargin != null) {
insets.left += outerMargin.left;
insets.right += outerMargin.right;
insets.top += outerMargin.top;
insets.bottom += outerMargin.bottom;
}
return insets;
}
/**
* Returns the insets of the original border (without the margin! Beware of
* {@link MarginBorder}!).
*
* @return the insets of the border delegate
*/
public Insets getRealBorderInsets() {
if (borderDelegate == null) {
//SwingX 1287: null borders are possible and give no insets
return new Insets(0, 0, 0, 0);
}
Insets insets = borderDelegate.getBorderInsets(textField);
// for some reason, all LnFs add the margin to the insets.
// we want the insets without the margin, so substract the margin here!!
// TODO: consider checking, if the current border really includes the
// margin. Consider:
// 1. Not only MarginBorder adds margin
// 2. Calling getBorderInsets(null) is not appropriate, since some
// Borders can't handle null values.
Insets margin = textField.getMargin();
if (margin != null) {
insets.left -= margin.left;
insets.right -= margin.right;
insets.top -= margin.top;
insets.bottom -= margin.bottom;
}
return insets;
}
public boolean isBorderOpaque() {
if (borderDelegate == null) {
return false;
}
return borderDelegate.isBorderOpaque();
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
if (borderDelegate != null) {
borderDelegate.paintBorder(c, g, x, y, width, height);
}
}
public void propertyChange(PropertyChangeEvent evt) {
replaceBorderIfNecessary();
}
public void uninstall() {
if (textField != null) {
textField.removePropertyChangeListener("border", this);
if (textField.getBorder() == this) {
textField.setBorder(borderDelegate);
}
textField.setLayout(null);
textField = null;
}
}
@Override
public String toString() {
return String.format("%s (%s): %s", getClass().getName(), getBorderInsets(null), borderDelegate);
}
}