All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdesktop.swingx.JXPanel Maven / Gradle / Ivy

There is a newer version: 1.7.2
Show newest version
/*
 * $Id: JXPanel.java 4280 2013-02-21 17:35:03Z kschaefe $
 *
 * 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;

import org.jdesktop.beans.JavaBean;
import org.jdesktop.swingx.painter.AbstractPainter;
import org.jdesktop.swingx.painter.Painter;
import org.jdesktop.swingx.util.Contract;

import javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.synth.SynthLookAndFeel;
import java.awt.AlphaComposite;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeListener;

import static org.jdesktop.swingx.util.GraphicsUtilities.createCompatibleTranslucentImage;

/**
 * 

* An extended {@code JPanel} that provides additional features. *

*

Scrollable

*

* {@code JXPanel} is {@link Scrollable} by default. It provides reasonable implementations of all * of the interface methods. In addition, it supports the setting of common scrolling approaches * defined in {@link ScrollableSizeHint}. *

*

Alpha Support

*

* {@code JXPanel} has full alpha-channel support. This means that the {@code JXPanel} can be made * fully or partially transparent. This means that the JXPanel and all of its children will behave * as a single paint at the specified alpha value. Cauton: best practice is to use * either alpha support or opacity support, but not both. See the documentation on the methods for * further information. *

*

* A transparency example, this following code will show the black background of the parent: * *

 * JXPanel panel = new JXPanel();
 * panel.add(new JButton("Push Me"));
 * panel.setAlpha(.5f);
 *
 * container.setBackground(Color.BLACK);
 * container.add(panel);
 * 
* *

Painter Support

*

* {@code JXPanel} has support for {@linkplain Painter}s. *

*

* A painter example, this following code will show how to add a simple painter: * *

 * JXPanel panel = new JXPanel();
 * panel.setBackgroundPainter(new PinstripePainter());
 * 
* * @author rbair * @see Scrollable * @see Painter */ @JavaBean @SuppressWarnings("nls") public class JXPanel extends JPanel implements AlphaPaintable, BackgroundPaintable, Scrollable { private ScrollableSizeHint scrollableWidthHint = ScrollableSizeHint.FIT; private ScrollableSizeHint scrollableHeightHint = ScrollableSizeHint.FIT; /** * The alpha level for this component. */ private volatile float alpha = 1.0f; /** * If the old alpha value was 1.0, I keep track of the opaque setting because * a translucent component is not opaque, but I want to be able to restore * opacity to its default setting if the alpha is 1.0. Honestly, I don't know * if this is necessary or not, but it sounded good on paper :) *

TODO: Check whether this variable is necessary or not

*/ private boolean oldOpaque; private float oldAlpha = 1f; /** * Indicates whether this component should inherit its parent alpha value */ private boolean inheritAlpha = true; /** * Specifies the Painter to use for painting the background of this panel. * If no painter is specified, the normal painting routine for JPanel * is called. Old behavior is also honored for the time being if no * backgroundPainter is specified */ @SuppressWarnings("rawtypes") private Painter backgroundPainter; private boolean paintBorderInsets = true; /** * The listener installed on the current backgroundPainter, if any. */ private PropertyChangeListener painterChangeListener; /** * Creates a new JXPanel with a double buffer * and a flow layout. */ public JXPanel() { } /** * Creates a new JXPanel with FlowLayout * and the specified buffering strategy. * If isDoubleBuffered is true, the JXPanel * will use a double buffer. * * @param isDoubleBuffered a boolean, true for double-buffering, which * uses additional memory space to achieve fast, flicker-free * updates */ public JXPanel(boolean isDoubleBuffered) { super(isDoubleBuffered); } /** * Create a new buffered JXPanel with the specified layout manager * * @param layout the LayoutManager to use */ public JXPanel(LayoutManager layout) { super(layout); } /** * Creates a new JXPanel with the specified layout manager and buffering * strategy. * * @param layout the LayoutManager to use * @param isDoubleBuffered a boolean, true for double-buffering, which * uses additional memory space to achieve fast, flicker-free * updates */ public JXPanel(LayoutManager layout, boolean isDoubleBuffered) { super(layout, isDoubleBuffered); } /** * {@inheritDoc} *

* Setting the component to be opaque will reset the alpha setting to {@code 1f} (full * opaqueness). Setting the component to be non-opaque will restore the previous alpha * transparency. If the component is non-opaque with a fully-opaque alpha value ({@code 1f}), * the behavior should be the same as as a {@code JPanel} that is non-opaque. */ @Override public void setOpaque(boolean opaque) { if (isPatch()) { setOpaquePatch(opaque); return; } if (opaque) { oldAlpha = getAlpha(); if (oldAlpha < 1f) { setAlpha(1f); } else { super.setOpaque(true); repaint(); } } else if (getAlpha() == 1f) { if (oldAlpha == 1f) { super.setOpaque(false); repaint(); } else { setAlpha(oldAlpha); } } } @Override public boolean isOpaque() { if (isPatch()) { return isOpaquePatch(); } return super.isOpaque(); } @Override public float getAlpha() { return alpha; } @Override public void setAlpha(float alpha) { if (isPatch()) { setAlphaPatch(alpha); return; } if (alpha < 0f || alpha > 1f) { throw new IllegalArgumentException("invalid alpha value " + alpha); } float oldValue = getAlpha(); this.alpha = alpha; if (getAlpha() < 1f) { if (oldValue == 1) { //it used to be 1, but now is not. Save the oldOpaque oldOpaque = isOpaque(); super.setOpaque(false); } installRepaintManager(); } else { uninstallRepaintManager(); //restore the oldOpaque if it was true (since opaque is false now) if (oldOpaque) { super.setOpaque(true); } } firePropertyChange("alpha", oldValue, getAlpha()); repaint(); } /** * experimental version: doesn't tweak opaque *

* called if isPatch * * @param alpha */ private void setAlphaPatch(float alpha) { if (alpha < 0f || alpha > 1f) { throw new IllegalArgumentException("invalid alpha value " + alpha); } float oldValue = getAlpha(); this.alpha = alpha; if (getAlpha() < 1f) { if (oldValue == 1) { //it used to be 1, but now is not. Save the oldOpaque oldOpaque = isOpaque(); // super.setOpaque(false); } installRepaintManager(); } else { uninstallRepaintManager(); //restore the oldOpaque if it was true (since opaque is false now) if (oldOpaque) { // super.setOpaque(true); } } firePropertyChange("alpha", oldValue, getAlpha()); repaint(); } void installRepaintManager() { } void uninstallRepaintManager() { } @Override public float getEffectiveAlpha() { float a = getAlpha(); if (isInheritAlpha()) { for (Component c = getParent(); c != null; c = c.getParent()) { if (c instanceof AlphaPaintable) { a = Math.min(((AlphaPaintable) c).getEffectiveAlpha(), a); break; } } } return a; } @Override public boolean isInheritAlpha() { return inheritAlpha; } @Override public void setInheritAlpha(boolean val) { boolean oldValue = isInheritAlpha(); inheritAlpha = val; firePropertyChange("inheritAlpha", oldValue, isInheritAlpha()); } /** * Sets the horizontal sizing hint. The hint is used by the Scrollable implementation * to service the getScrollableTracksWidth. * * @param hint the horizontal sizing hint, must not be null * and must be vertical. * @throws NullPointerException if null * @see #setScrollableHeightHint(ScrollableSizeHint) * @see ScrollableSizeHint */ public final void setScrollableWidthHint(ScrollableSizeHint hint) { Contract.asNotNull(hint, "hint cannot be null"); ScrollableSizeHint oldValue = getScrollableWidthHint(); if (oldValue == hint) return; this.scrollableWidthHint = hint; revalidate(); firePropertyChange("scrollableWidthHint", oldValue, getScrollableWidthHint()); } /** * Sets the vertical sizing hint. The hint is used by the Scrollable implementation * to service the getScrollableTracksHeight. * * @param hint the vertical sizing hint, must not be null * and must be vertical. * @throws NullPointerException if null * @see #setScrollableWidthHint(ScrollableSizeHint) * @see ScrollableSizeHint */ public final void setScrollableHeightHint(ScrollableSizeHint hint) { Contract.asNotNull(hint, "hint cannot be null"); ScrollableSizeHint oldValue = getScrollableHeightHint(); if (oldValue == hint) return; this.scrollableHeightHint = hint; revalidate(); firePropertyChange("scrollableHeightHint", oldValue, getScrollableHeightHint()); } protected ScrollableSizeHint getScrollableWidthHint() { return scrollableWidthHint; } protected ScrollableSizeHint getScrollableHeightHint() { return scrollableHeightHint; } /** * {@inheritDoc} */ @Override public boolean getScrollableTracksViewportHeight() { return scrollableHeightHint.getTracksParentSize(this, SwingConstants.VERTICAL); } /** * {@inheritDoc} */ @Override public boolean getScrollableTracksViewportWidth() { return scrollableWidthHint.getTracksParentSize(this, SwingConstants.HORIZONTAL); } /** * {@inheritDoc} */ @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } /** * {@inheritDoc} */ @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { if (orientation == SwingConstants.VERTICAL) { return visibleRect.height; } else if (orientation == SwingConstants.HORIZONTAL) { return visibleRect.width; } else { throw new IllegalArgumentException("invalid orientation"); //$NON-NLS-1$ } } /** * {@inheritDoc} */ @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return getScrollableBlockIncrement(visibleRect, orientation, direction) / 10; } /** * Sets the vertical size tracking to either ScrollableSizeTrack.FIT or NONE, if the * boolean parameter is true or false, respectively.

* * NOTE: this method is kept for backward compatibility only, for full * control use setScrollableHeightHint. * * @param scrollableTracksViewportHeight The scrollableTracksViewportHeight to set. * @see #setScrollableHeightHint(ScrollableSizeHint) */ public void setScrollableTracksViewportHeight(boolean scrollableTracksViewportHeight) { setScrollableHeightHint(scrollableTracksViewportHeight ? ScrollableSizeHint.FIT : ScrollableSizeHint.NONE); } /** * Sets the horizontal size tracking to either ScrollableSizeTrack.FIT or NONE, if the * boolean parameter is true or false, respectively.

* * NOTE: this method is kept for backward compatibility only, for full * control use setScrollableWidthHint. * * @param scrollableTracksViewportWidth The scrollableTracksViewportWidth to set. * @see #setScrollableWidthHint(ScrollableSizeHint) */ public void setScrollableTracksViewportWidth(boolean scrollableTracksViewportWidth) { setScrollableWidthHint(scrollableTracksViewportWidth ? ScrollableSizeHint.FIT : ScrollableSizeHint.NONE); } /** * Sets a Painter to use to paint the background of this JXPanel. * * @param p the new painter * @see #getBackgroundPainter() */ @Override public void setBackgroundPainter(Painter p) { Painter old = getBackgroundPainter(); if (old instanceof AbstractPainter) { ((AbstractPainter) old).removePropertyChangeListener(painterChangeListener); } backgroundPainter = p; if (backgroundPainter instanceof AbstractPainter) { ((AbstractPainter) backgroundPainter).addPropertyChangeListener(getPainterChangeListener()); } firePropertyChange("backgroundPainter", old, getBackgroundPainter()); repaint(); } /** * @return a listener for painter change events */ protected PropertyChangeListener getPainterChangeListener() { if (painterChangeListener == null) { painterChangeListener = evt -> repaint(); } return painterChangeListener; } /** * Returns the current background painter. The default value of this property * is a painter which draws the normal JPanel background according to the current look and feel. * * @return the current painter * @see #setBackgroundPainter(Painter) * @see #isPaintBorderInsets() */ @Override public Painter getBackgroundPainter() { return backgroundPainter; } /** * Returns true if the background painter should paint where the border is * or false if it should only paint inside the border. This property is * true by default. This property affects the width, height, * and initial transform passed to the background painter. */ @Override public boolean isPaintBorderInsets() { return paintBorderInsets; } /** * Sets the paintBorderInsets property. * Set to true if the background painter should paint where the border is * or false if it should only paint inside the border. This property is true by default. * This property affects the width, height, * and initial transform passed to the background painter. *

* This is a bound property. */ @Override public void setPaintBorderInsets(boolean paintBorderInsets) { boolean old = this.isPaintBorderInsets(); this.paintBorderInsets = paintBorderInsets; firePropertyChange("paintBorderInsets", old, isPaintBorderInsets()); } //support for Java 7 painting improvements protected boolean isPaintingOrigin() { return getAlpha() < 1f; } /** * Overridden paint method to take into account the alpha setting. * * @param g the Graphics context in which to paint */ @Override public void paint(Graphics g) { //short circuit painting if no transparency if (getAlpha() == 1f) { super.paint(g); } else { //the component is translucent, so we need to render to //an intermediate image before painting // TODO should we cache this image? repaint to same image unless size changes? BufferedImage img = createCompatibleTranslucentImage(getWidth(), getHeight()); Graphics2D gfx = img.createGraphics(); try { super.paint(gfx); } finally { gfx.dispose(); } Graphics2D g2d = (Graphics2D) g; Composite oldComp = g2d.getComposite(); try { Composite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getEffectiveAlpha()); g2d.setComposite(alphaComp); //TODO should we cache the image? g2d.drawImage(img, null, 0, 0); } finally { g2d.setComposite(oldComp); } } } /** * Overridden to provide Painter support. It will call backgroundPainter.paint() * if it is not null, else it will call super.paintComponent(). * * @param g the Graphics context in which to paint */ @Override @SuppressWarnings("unchecked") protected void paintComponent(Graphics g) { if (isPatch()) { paintComponentPatch(g); return; } Graphics2D g2 = (Graphics2D) g.create(); try { // we should be painting the background behind the painter if we have one // this prevents issues with buffer reuse where visual artifacts sneak in if (isOpaque() || UIManager.getLookAndFeel() instanceof SynthLookAndFeel) { //this will paint the foreground if a JXPanel subclass is //unfortunate enough to have one super.paintComponent(g2); } else if (getAlpha() < 1f) { g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); } if (getBackgroundPainter() != null) { if (isPaintBorderInsets()) { getBackgroundPainter().paint(g2, this, getWidth(), getHeight()); } else { Insets insets = getInsets(); g.translate(insets.left, insets.top); getBackgroundPainter().paint( g2, this, getWidth() - insets.left - insets.right, getHeight() - insets.top - insets.bottom ); g.translate(-insets.left, -insets.top); } } //force the foreground to paint again...workaround for folks that //incorrectly extend JXPanel instead of JComponent getUI().paint(g2, this); } finally { g2.dispose(); } } //--------------------- experimental patch protected boolean isPatch() { return Boolean.TRUE.equals(UIManager.get("JXPanel.patch")); } private boolean fakeTransparent; @SuppressWarnings("unchecked") protected void paintComponentPatch(Graphics g) { Graphics2D g2 = (Graphics2D) g.create(); try { if (isPaintingBackground()) { g2.setColor(getBackground()); g2.fillRect(0, 0, getWidth(), getHeight()); } if (getBackgroundPainter() != null) { getBackgroundPainter().paint(g2, this, getWidth(), getHeight()); } fakeTransparent = true; getUI().update(g2, this); } finally { g2.dispose(); fakeTransparent = false; } } protected boolean isOpaquePatch() { if (fakeTransparent) return false; if (isPaintingBackground()) { return !isTransparentBackground() && !isAlpha(); } return false; } protected void setOpaquePatch(boolean opaque) { super.setOpaque(opaque); } /** * Returns whether or not the container hierarchy below is * transparent. */ protected boolean isAlpha() { // PENDING JW: use effective alpha? return getAlpha() < 1.0f; } /** * Returns whether or not the background is transparent. */ protected boolean isTransparentBackground() { return getBackground().getAlpha() < 255; } /** * Returns whether or not the background should be painted. */ protected boolean isPaintingBackground() { return super.isOpaque(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy