FadingPopup

package com.gossamer.voyant.client.ui.dialog;

import com.gossamer.voyant.client.AppStyle;

import com.gossamer.voyant.client.Voyant;

import com.gossamer.voyant.client.swing.FormDesignerPanel;

import org.jdesktop.animation.timing.Animator;

import org.jdesktop.animation.timing.interpolation.PropertySetter;

import org.jdesktop.swinghelper.layer.JXLayer;

import org.jdesktop.swinghelper.layer.painter.AbstractPainter;

import javax.swing.*;

import javax.swing.text.BadLocationException;

import javax.swing.text.Style;

import javax.swing.text.StyleContext;

import javax.swing.text.StyledDocument;

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.event.MouseAdapter;

import java.awt.event.MouseEvent;

import java.awt.geom.Rectangle2D;

import java.awt.image.BufferedImage;

/**

*

*

*/

public class FadingPopup extends JPanel {

private float alpha = 1.0f;

private BufferedImage img;

private Dimension dimension;

private JComponent content;

private Animator animator;

private int fadeTime;

private int fadeDelay = 1000;

private float acceleration = 0.2f;

private float deceleration = 0.4f;

private boolean fading; // Indicates that an animator is currently fading out this popup

/**

* Default fade time of 3000 milliseconds

* @param text text you want in the popup

*/

public static void popup(String text) {

popup(makeContent(text));

}

/**

* Default fade time of 3000 milliseconds

* @param content the JComponent you want displayed in the popup. Add a FadingPopupListener to any components to which you want click events passed.

*/

public static void popup(JComponent content) {

new FadingPopup(content).popup(3000);

}

public static void popup(int fadeTime, String text) {

popup(fadeTime, makeContent(text));

}

/**

* @param fadeTime

* @param content the JComponent you want displayed in the popup. Add a FadingPopupListener to any components to which you want click events passed.

*/

public static void popup(int fadeTime, JComponent content) {

new FadingPopup(content).popup(fadeTime);

}

/**

* @param fadeTime

* @param content the JComponent you want displayed in the popup. Add a FadingPopupListener to any components to which you want click events passed.

* @param x The x value will be the right-side coordinate of the popup

*/

public static void popupToTheLeftOf(int fadeTime, JComponent content, int x) {

new FadingPopup(content).popupToTheLeftOf(fadeTime, x);

}

private static JComponent makeContent(String text) {

if (text == null) {

return new JLabel();

}

FormDesignerPanel form = new FormDesignerPanel("com/gossamer/voyant/client/forms/dialogs/fadingPopup.jfd");

JTextPane textArea = form.getTextPane("textPane");

JPanel underIcon = form.getPanel("underIcon");

JLabel icon = form.getLabel("icon");

StyledDocument doc = textArea.getStyledDocument();

Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);

try {

doc.remove(0, doc.getLength());

doc.insertString(doc.getLength(), text, def);

} catch (BadLocationException e) {

}

textArea.setFont(new Font("Arial", Font.PLAIN, 12));

return form;

}

public FadingPopup(String text) {

this(makeContent(text));

}

public FadingPopup(final JComponent content) {

setLayout(new GridBagLayout());

GridBagConstraints gc = new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(7, 2, 1, 1), 0, 0);

this.content = content;

img = AppStyle.getInstance().popupBackground;

dimension = new Dimension(img.getWidth(), img.getHeight());

final JXLayer layer = new JXLayer(content);

layer.setPainter(new TransparentPainter<JComponent>());

add(layer, gc);

layer.getGlassPane().addMouseListener(new MouseAdapter() {

@Override

public void mouseEntered(MouseEvent e) {

cancelFade();

}

@Override

public void mouseExited(MouseEvent e) {

fadeQuick();

}

@Override

public void mouseClicked(MouseEvent e) {

// First, pass events down into any components that are listening - if they're listening with a FadingPopupListener

Component c = SwingUtilities.getDeepestComponentAt(layer.getView(), e.getX(), e.getY());

if (c instanceof AbstractButton) {

AbstractButton b = (AbstractButton) c;

for (ActionListener al : b.getActionListeners()) {

if (al instanceof FadingPopupListener) {

al.actionPerformed(new ActionEvent(c, 0, ""));

}

}

}

dismiss();

}

});

}

@Override

public Dimension getPreferredSize() {

return dimension;

}

public void popup(int fadeTime) {

popupToTheLeftOf(fadeTime, Voyant.getFrame().getSize().width);

}

public void popupToTheLeftOf(int fadeTime, int x) {

popup(fadeTime, x - img.getWidth() - 17);

}

private void popup(int fadeTime, int x) {

this.fadeTime = fadeTime;

Voyant.getFrame().showOnTopOnGlassPane(this, x, getPopupY(), false);

doLayout();

validate();

repaint();

fade();

}

public void setAlpha(float alpha) {

this.alpha = alpha;

if (alpha <= 0.01f) {

dismiss();

}

repaint();

}

public float getAlpha() {

return this.alpha;

}

@Override

public void paint(Graphics g) {

BufferedImage img2 = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.OPAQUE);

Graphics2D imgGraphics = img2.createGraphics();

Graphics2D panelGraphics = (Graphics2D) g.create();

imgGraphics.drawImage(img, 0, 0, null);

imgGraphics.setClip(new Rectangle2D.Double(0, 0, img.getWidth() - 2, img.getHeight() - 2));

imgGraphics.dispose();

panelGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));

panelGraphics.drawImage(img2, 0, 0, null);

setOpaque(false);

content.setOpaque(false);

super.paint(panelGraphics);

panelGraphics.dispose();

}

private int getPopupY() {

return Voyant.getFrame().getSize().height - img.getHeight() - 44;

}

private void fade() {

fade(fadeTime);

}

private void fadeQuick() {

setAlpha(0.8f);

fade(fadeTime);

}

private synchronized void fade(int fadeTime) {

if (fading) {

return;

}

animator = new Animator(fadeTime);

animator.addTarget(new PropertySetter(this, "alpha", 0.0f));

animator.setStartDelay(fadeDelay);

animator.setAcceleration(acceleration);

animator.setDeceleration(deceleration);

animator.start();

fading = true;

}

private synchronized void dismiss() {

setVisible(false);

Voyant.getFrame().removeFromGlassPane(this);

animator.cancel();

fading = false;

}

private synchronized void cancelFade() {

animator.cancel();

setAlpha(1.0f);

fading = false;

fadeDelay = 0; // Set to zero now that they've moused over it once. Don't make em wait in the future.

}

private static class TransparentPainter<V extends JComponent>

extends AbstractPainter<V> {

public void paint(Graphics2D g2, JXLayer<V> l) {

l.setOpaque(false);

l.paint(g2);

if (!l.isEnabled()) {

g2.setColor(new Color(0.0f, 0.0f, 0.0f, 0.0f));

g2.fillRect(0, 0, l.getWidth(), l.getHeight());

}

}

}

public static interface FadingPopupListener extends ActionListener {}

}