
otify.2.14.source-code.NotifyPopup Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Notify Show documentation
Show all versions of Notify Show documentation
Linux, MacOS, or Windows (notification/growl/toast/) popups for the desktop for Java 8+
/*
* Copyright 2015 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.notify;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import dorkbox.tweenengine.BaseTween;
import dorkbox.tweenengine.Tween;
import dorkbox.tweenengine.TweenCallback;
import dorkbox.tweenengine.TweenEquations;
import dorkbox.tweenengine.TweenManager;
import dorkbox.util.ActionHandlerLong;
import dorkbox.util.OS;
import dorkbox.util.Property;
import dorkbox.util.ScreenUtil;
import dorkbox.util.SwingUtil;
import dorkbox.util.swing.SwingActiveRender;
// we can't use regular popup, because if we have no owner, it won't work!
// instead, we just create a JFrame and use it to hold our content
@SuppressWarnings({"Duplicates", "FieldCanBeLocal"})
public
class NotifyPopup extends JFrame {
private static final long serialVersionUID = 1L;
@Property
/** This is the title font used by a notification. */
public static String TITLE_TEXT_FONT = "Source Code Pro BOLD 16";
@Property
/** This is the main text font used by a notification. */
public static String MAIN_TEXT_FONT = "Source Code Pro BOLD 12";
@Property
/** How long we want it to take for the popups to relocate when one is closed */
public static float MOVE_DURATION = 1.0F;
private static final int padding = 40;
private static final Map> popups = new HashMap>();
private static final NotifyPopupAccessor accessor = new NotifyPopupAccessor();
private static final TweenManager tweenManager = new TweenManager();
private static ActionHandlerLong frameStartHandler;
private static WindowUtil opacity_compat;
static {
// this is for updating the tween engine during active-rendering
frameStartHandler = new ActionHandlerLong() {
@Override
public
void handle(final long deltaInNanos) {
NotifyPopup.tweenManager.update(deltaInNanos);
}
};
if (OS.javaVersion == 6) {
opacity_compat = new WindowUtil_Java6();
} else {
opacity_compat = new WindowUtil_Java7plus();
}
}
private static final int WIDTH = 300;
private static final int HEIGHT = 87;
private static final int PROGRESS_HEIGHT = HEIGHT - 1;
private static final Stroke stroke = new BasicStroke(2);
private static final int closeX = 282;
private static final int closeY = 2;
private static final int Y_1 = closeY + 5;
private static final int X_1 = closeX + 5;
private static final int Y_2 = closeY + 11;
private static final int X_2 = closeX + 11;
private final Color panel_BG;
private final Color titleText_FG;
private final Color mainText_FG;
private final Color closeX_FG;
private final Color progress_FG;
private final int anchorX;
private final int anchorY;
private final WindowAdapter windowListener;
private final MouseAdapter mouseListener;
private final Notify notification;
private final ImageIcon imageIcon;
// this is used in combination with position, so that we can track which screen and what position a popup is in
private final String idAndPosition;
private int popupIndex;
private Tween tween = null;
private Tween hideTween = null;
// for the progress bar. we directly draw this onscreen
// non-volatile because it's always accessed in the active render thread
private int progress = 0;
private final boolean showCloseButton;
private BufferedImage cachedImage;
private static final Random RANDOM = new Random();
// this is on the swing EDT
@SuppressWarnings("NumericCastThatLosesPrecision")
NotifyPopup(Notify notification, Image image, ImageIcon imageIcon) {
this.notification = notification;
this.imageIcon = imageIcon;
windowListener = new NotifyPopupWindowAdapter();
mouseListener = new NotifyPopupClickAdapter();
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
setUndecorated(true);
setOpacity_Compat(1.0F);
setAlwaysOnTop(false);
setAlwaysOnTop(true);
setLayout(null);
setSize(WIDTH, HEIGHT);
setLocation(Short.MIN_VALUE, Short.MIN_VALUE);
setTitle(notification.title);
setResizable(false);
if (image != null) {
setIconImage(image);
} else {
setIconImage(SwingUtil.BLANK_ICON);
}
addWindowListener(windowListener);
addMouseListener(mouseListener);
if (notification.isDark) {
panel_BG = Color.DARK_GRAY;
titleText_FG = Color.GRAY;
mainText_FG = Color.LIGHT_GRAY;
closeX_FG = Color.GRAY;
progress_FG = Color.gray;
}
else {
panel_BG = Color.WHITE;
titleText_FG = Color.GRAY.darker();
mainText_FG = Color.GRAY;
closeX_FG = Color.LIGHT_GRAY;
progress_FG = new Color(0x42A5F5);
}
setBackground(panel_BG);
showCloseButton = !notification.hideCloseButton;
GraphicsDevice device;
if (notification.screenNumber == Short.MIN_VALUE) {
// set screen position based on mouse
Point mouseLocation = MouseInfo.getPointerInfo()
.getLocation();
device = ScreenUtil.getGraphicsDeviceAt(mouseLocation);
}
else {
// set screen position based on specified screen
int screenNumber = notification.screenNumber;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice screenDevices[] = ge.getScreenDevices();
if (screenNumber < 0) {
screenNumber = 0;
}
else if (screenNumber > screenDevices.length - 1) {
screenNumber = screenDevices.length - 1;
}
device = screenDevices[screenNumber];
}
idAndPosition = device.getIDstring() + notification.position;
Rectangle screenBounds = device.getDefaultConfiguration()
.getBounds();
// we use the screen that the mouse is currently on.
final int startX = (int) screenBounds.getX();
final int startY = (int) screenBounds.getY();
final int screenWidth = (int) screenBounds.getWidth();
final int screenHeight = (int) screenBounds.getHeight();
// determine location for the popup
final Pos position = notification.position;
// get anchorX
switch (position) {
case TOP_LEFT:
case BOTTOM_LEFT:
anchorX = startX + padding;
break;
case CENTER:
anchorX = startX + (screenWidth / 2) - WIDTH / 2 - padding / 2;
break;
case TOP_RIGHT:
case BOTTOM_RIGHT:
anchorX = startX + screenWidth - WIDTH - padding;
break;
default:
throw new RuntimeException("Unknown position. '" + position + "'");
}
// get anchorY
switch (position) {
case TOP_LEFT:
case TOP_RIGHT:
anchorY = padding + startY;
break;
case CENTER:
anchorY = startY + (screenHeight / 2) - HEIGHT / 2 - padding / 2;
break;
case BOTTOM_LEFT:
case BOTTOM_RIGHT:
anchorY = startY + screenHeight - HEIGHT - padding;
break;
default:
throw new RuntimeException("Unknown position. '" + position + "'");
}
}
@Override
public
void paint(Graphics g) {
// we cache the text + image (to another image), and then always render the close + progressbar
int width = getWidth();
int height = getHeight();
if (width <= 0 || height <= 0) {
return;
}
if (cachedImage == null) {
cachedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g2 = cachedImage.createGraphics();
try {
g2.setColor(panel_BG);
g2.fillRect(0, 0, WIDTH, HEIGHT);
// Draw the title text
Font titleTextFont = SwingUtil.parseFont(TITLE_TEXT_FONT);
g2.setColor(titleText_FG);
g2.setFont(titleTextFont);
g2.drawString(notification.title, 5, 20);
int posX = 10;
int textLengthLimit = 108;
// ICON
if (imageIcon != null) {
textLengthLimit = 88;
posX = 60;
// Draw the image
imageIcon.paintIcon(this, g2, 5, 30);
}
// Draw the main text
Font mainTextFont = SwingUtil.parseFont(MAIN_TEXT_FONT);
String notText = notification.text;
int length = notText.length();
StringBuilder text = new StringBuilder(length);
// are we "html" already? just check for the starting tag and strip off END html tag
if (length >= 13 && notText.regionMatches(true, length-7, "