org.jdesktop.swingx.plaf.basic.BasicHeaderUI Maven / Gradle / Ivy
Show all versions of swingx-all Show documentation
/*
* $Id: BasicHeaderUI.java 3927 2011-02-22 16:34:11Z kleopatra $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jdesktop.swingx.plaf.basic;
import org.jdesktop.swingx.JXHeader;
import org.jdesktop.swingx.JXHeader.IconPosition;
import org.jdesktop.swingx.JXLabel;
import org.jdesktop.swingx.JXLabel.MultiLineSupport;
import org.jdesktop.swingx.painter.MattePainter;
import org.jdesktop.swingx.painter.Painter;
import org.jdesktop.swingx.plaf.HeaderUI;
import org.jdesktop.swingx.plaf.PainterUIResource;
import org.jdesktop.swingx.plaf.UIManagerExt;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.text.View;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.HierarchyBoundsAdapter;
import java.awt.event.HierarchyBoundsListener;
import java.awt.event.HierarchyEvent;
import java.beans.PropertyChangeListener;
/**
* Base implementation of Header
UI.
*
* PENDING JW: This implementation is unusual in that it does not keep a reference
* to the component it controls. Typically, such is only the case if the ui is
* shared between instances. Historical? A consequence is that the un/install methods
* need to carry the header as parameter. Which looks funny when at the same time
* the children of the header are instance fields in this. Should think about cleanup:
* either get rid off the instance fields here, or reference the header and remove
* the param (would break subclasses).
*
* PENDING JW: keys for uidefaults are inconsistent - most have prefix "JXHeader." while
* defaultIcon has prefix "Header."
*
* @author rbair
* @author rah003
* @author Jeanette Winzenburg
*/
public class BasicHeaderUI extends HeaderUI {
// Implementation detail. Neeeded to expose getMultiLineSupport() method to allow restoring view
// lost after LAF switch
protected static class DescriptionPane extends JXLabel {
@Override
public void paint(Graphics g) {
// switch off jxlabel default antialiasing
// JW: that cost me dearly to track down - it's the default foreground painter
// which is an AbstractPainter which has _global_ antialiased on by default
// and here the _text_ antialiased is turned off
// changed JXLabel default foregroundPainter to have antialiasing false by
// default, so remove interference here
// part of fix for #920 - the other part is in JXLabel, fix 1164
// ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
// RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
super.paint(g);
}
@Override
public MultiLineSupport getMultiLineSupport() {
return super.getMultiLineSupport();
}
}
protected JLabel titleLabel;
protected DescriptionPane descriptionPane;
protected JLabel imagePanel;
private PropertyChangeListener propListener;
private HierarchyBoundsListener boundsListener;
private Color gradientLightColor;
private Color gradientDarkColor;
/**
* Creates a new instance of BasicHeaderUI
*/
public BasicHeaderUI() {
}
/**
* Returns an instance of the UI delegate for the specified component.
* Each subclass must provide its own static createUI
* method that returns an instance of that UI delegate subclass.
* If the UI delegate subclass is stateless, it may return an instance
* that is shared by multiple components. If the UI delegate is
* stateful, then it should return a new instance per component.
* The default implementation of this method throws an error, as it
* should never be invoked.
*/
public static ComponentUI createUI(JComponent c) {
return new BasicHeaderUI();
}
/**
* Configures the specified component appropriate for the look and feel.
* This method is invoked when the ComponentUI
instance is being installed
* as the UI delegate on the specified component. This method should
* completely configure the component for the look and feel,
* including the following:
*
* - Install any default property values for color, fonts, borders,
* icons, opacity, etc. on the component. Whenever possible,
* property values initialized by the client program should not
* be overridden.
*
- Install a
LayoutManager
on the component if necessary.
* - Create/add any required sub-components to the component.
*
- Create/install event listeners on the component.
*
- Create/install a
PropertyChangeListener
on the component in order
* to detect and respond to component property changes appropriately.
* - Install keyboard UI (mnemonics, traversal, etc.) on the component.
*
- Initialize any appropriate instance data.
*
*
* @param c the component where this UI delegate is being installed
* @see #uninstallUI
* @see JComponent#updateUI
*/
@Override
public void installUI(JComponent c) {
super.installUI(c);
assert c instanceof JXHeader;
JXHeader header = (JXHeader) c;
installDefaults(header);
installComponents(header);
installListeners(header);
}
/**
* Reverses configuration which was done on the specified component during
* installUI
. This method is invoked when this
* UIComponent
instance is being removed as the UI delegate
* for the specified component. This method should undo the
* configuration performed in installUI
, being careful to
* leave the JComponent
instance in a clean state (no
* extraneous listeners, look-and-feel-specific property objects, etc.).
* This should include the following:
*
* - Remove any UI-set borders from the component.
*
- Remove any UI-set layout managers on the component.
*
- Remove any UI-added sub-components from the component.
*
- Remove any UI-added event/property listeners from the component.
*
- Remove any UI-installed keyboard UI from the component.
*
- Nullify any allocated instance data objects to allow for GC.
*
*
* @param c the component from which this UI delegate is being removed;
* this argument is often ignored,
* but might be used if the UI object is stateless
* and shared by multiple components
* @see #installUI
* @see JComponent#updateUI
*/
@Override
public void uninstallUI(JComponent c) {
assert c instanceof JXHeader;
JXHeader header = (JXHeader) c;
uninstallListeners(header);
uninstallComponents(header);
uninstallDefaults(header);
}
/**
* Installs default header properties.
*
*
* NOTE: this method is called before the children are created, so must not
* try to access any of those!.
*
* @param header the header to install.
*/
protected void installDefaults(JXHeader header) {
gradientLightColor = UIManagerExt.getColor("JXHeader.startBackground");
if (gradientLightColor == null) {
// fallback to white
gradientLightColor = Color.WHITE;
}
gradientDarkColor = UIManagerExt.getColor("JXHeader.background");
// for backwards compatibility (mostly for substance and synthetica,
// I suspect) I'll fall back on the "control" color if
// JXHeader.background
// isn't specified.
if (gradientDarkColor == null) {
gradientDarkColor = UIManagerExt.getColor("control");
}
if (isUIInstallable(header.getBackgroundPainter())) {
header.setBackgroundPainter(createBackgroundPainter());
}
// title properties
if (isUIInstallable(header.getTitleFont())) {
Font titleFont = UIManager.getFont("JXHeader.titleFont");
// fallback to label font
header.setTitleFont(titleFont != null ? titleFont : UIManager.getFont("Label.font"));
}
if (isUIInstallable(header.getTitleForeground())) {
Color titleForeground = UIManagerExt.getColor("JXHeader.titleForeground");
// fallback to label foreground
header.setTitleForeground(titleForeground != null ? titleForeground : UIManagerExt.getColor("Label.foreground"));
}
// description properties
if (isUIInstallable(header.getDescriptionFont())) {
Font descFont = UIManager.getFont("JXHeader.descriptionFont");
// fallback to label font
header.setDescriptionFont(descFont != null ? descFont : UIManager.getFont("Label.font"));
}
if (isUIInstallable(header.getDescriptionForeground())) {
Color descForeground = UIManagerExt.getColor("JXHeader.descriptionForeground");
// fallback to label foreground
header.setDescriptionForeground(descForeground != null ? descForeground : UIManagerExt.getColor("Label.foreground"));
}
// icon label properties
if (isUIInstallable(header.getIcon())) {
header.setIcon(UIManager.getIcon("Header.defaultIcon"));
}
}
/**
* Uninstalls the given header's default properties. This implementation
* does nothing.
*
* @param h the header to ininstall the properties from.
*/
protected void uninstallDefaults(JXHeader h) {
}
/**
* Creates, configures, adds contained components.
* PRE: header's default properties must be set before calling this.
*
* @param header the header to install the components into.
*/
protected void installComponents(JXHeader header) {
titleLabel = new JLabel();
descriptionPane = new DescriptionPane();
imagePanel = new JLabel();
installComponentDefaults(header);
header.setLayout(new GridBagLayout());
resetLayout(header);
}
/**
* Unconfigures, removes and nulls contained components.
*
* @param header the header to install the components into.
*/
protected void uninstallComponents(JXHeader header) {
uninstallComponentDefaults(header);
header.remove(titleLabel);
header.remove(descriptionPane);
header.remove(imagePanel);
titleLabel = null;
descriptionPane = null;
imagePanel = null;
}
/**
* Configures the component default properties from the given header.
*
* @param header the header to install the components into.
*/
protected void installComponentDefaults(JXHeader header) {
// JW: force a not UIResource for properties which have ui default values
// like color, font, ??
titleLabel.setFont(getAsNotUIResource(header.getTitleFont()));
titleLabel.setForeground(getAsNotUIResource(header.getTitleForeground()));
titleLabel.setText(header.getTitle());
descriptionPane.setFont(getAsNotUIResource(header.getDescriptionFont()));
descriptionPane.setForeground(getAsNotUIResource(header.getDescriptionForeground()));
descriptionPane.setOpaque(false);
descriptionPane.setText(header.getDescription());
descriptionPane.setLineWrap(true);
imagePanel.setIcon(header.getIcon());
}
/**
* Returns a Font based on the param which is not of type UIResource.
*
* @param font the base font
* @return a font not of type UIResource, may be null.
*/
private static Font getAsNotUIResource(Font font) {
if (!(font instanceof UIResource))
return font;
// PENDING JW: correct way to create another font instance?
return font.deriveFont(font.getAttributes());
}
/**
* Returns a Color based on the param which is not of type UIResource.
*
* @param color the base color
* @return a color not of type UIResource, may be null.
*/
private static Color getAsNotUIResource(Color color) {
if (!(color instanceof UIResource))
return color;
// PENDING JW: correct way to create another color instance?
float[] rgb = color.getRGBComponents(null);
return new Color(rgb[0], rgb[1], rgb[2], rgb[3]);
}
/**
* Checks and returns whether the given property should be replaced
* by the UI's default value.
*
* PENDING JW: move as utility method ... where?
*
* @param property the property to check.
* @return true if the given property should be replaced by the UI#s
* default value, false otherwise.
*/
private static boolean isUIInstallable(Object property) {
return property == null || property instanceof UIResource;
}
/**
* Uninstalls component defaults. This implementation does nothing.
*
* @param header the header to uninstall from.
*/
protected void uninstallComponentDefaults(JXHeader header) {
}
protected void installListeners(JXHeader header) {
propListener = evt -> onPropertyChange(header, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
boundsListener = new HierarchyBoundsAdapter() {
@Override
public void ancestorResized(HierarchyEvent e) {
if (header == e.getComponent()) {
View v = (View) descriptionPane.getClientProperty(BasicHTML.propertyKey);
// view might get lost on LAF change ...
if (v == null) {
descriptionPane.putClientProperty(BasicHTML.propertyKey, MultiLineSupport.createView(descriptionPane));
v = (View) descriptionPane.getClientProperty(BasicHTML.propertyKey);
}
if (v != null) {
Container tla = header.getTopLevelAncestor();
if (tla == null) {
tla = header.getParent();
while (tla.getParent() != null) {
tla = tla.getParent();
}
}
int h = Math.max(descriptionPane.getHeight(), tla.getHeight());
int w = Math.min(tla.getWidth(), header.getParent().getWidth());
// 35 = description pane insets, TODO: obtain dynamically
w -= 35 +
header.getInsets().left + header.getInsets().right +
descriptionPane.getInsets().left + descriptionPane.getInsets().right +
imagePanel.getInsets().left + imagePanel.getInsets().right +
imagePanel.getWidth() + descriptionPane.getBounds().x;
v.setSize(w, h);
descriptionPane.setSize(w, (int) Math.ceil(v.getPreferredSpan(View.Y_AXIS)));
}
}
}
};
header.addPropertyChangeListener(propListener);
header.addHierarchyBoundsListener(boundsListener);
}
protected void uninstallListeners(JXHeader h) {
h.removePropertyChangeListener(propListener);
h.removeHierarchyBoundsListener(boundsListener);
}
protected void onPropertyChange(JXHeader h, String propertyName, Object oldValue, Object newValue) {
if ("title".equals(propertyName)) {
titleLabel.setText(h.getTitle());
} else if ("description".equals(propertyName)) {
descriptionPane.setText(h.getDescription());
} else if ("icon".equals(propertyName)) {
imagePanel.setIcon(h.getIcon());
} else if ("enabled".equals(propertyName)) {
boolean enabled = h.isEnabled();
titleLabel.setEnabled(enabled);
descriptionPane.setEnabled(enabled);
imagePanel.setEnabled(enabled);
} else if ("titleFont".equals(propertyName)) {
titleLabel.setFont((Font) newValue);
} else if ("descriptionFont".equals(propertyName)) {
descriptionPane.setFont((Font) newValue);
} else if ("titleForeground".equals(propertyName)) {
titleLabel.setForeground((Color) newValue);
} else if ("descriptionForeground".equals(propertyName)) {
descriptionPane.setForeground((Color) newValue);
} else if ("iconPosition".equals(propertyName)) {
resetLayout(h);
}
}
private void resetLayout(JXHeader h) {
h.remove(titleLabel);
h.remove(descriptionPane);
h.remove(imagePanel);
if (h.getIconPosition() == null || h.getIconPosition() == IconPosition.RIGHT) {
h.add(titleLabel, new GridBagConstraints(
0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 0, 11), 0, 0
));
h.add(descriptionPane, new GridBagConstraints(
0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(0, 24, 12, 11), 0, 0
));
h.add(imagePanel, new GridBagConstraints(
1, 0, 1, 2, 0.0, 1.0, GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE, new Insets(12, 0, 11, 11), 0, 0
));
} else {
h.add(titleLabel, new GridBagConstraints(
1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(12, 12, 0, 11), 0, 0
));
h.add(descriptionPane, new GridBagConstraints(
1, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH, new Insets(0, 24, 12, 11), 0, 0
));
h.add(imagePanel, new GridBagConstraints(
0, 0, 1, 2, 0.0, 1.0, GridBagConstraints.FIRST_LINE_END, GridBagConstraints.NONE, new Insets(12, 11, 0, 11), 0, 0
));
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
protected Painter createBackgroundPainter() {
MattePainter p = new MattePainter(new GradientPaint(0, 0, gradientLightColor, 1, 0, gradientDarkColor));
p.setPaintStretched(true);
return new PainterUIResource(p);
}
}