Better caching of images, better api (change graphic -> image) to make

more sense. Set the default image names to be static strings, so it's
easier to know what they are without knowing specific details.
This commit is contained in:
nathan 2017-09-03 17:34:09 +02:00
parent cdbf8a2a4d
commit 289e83ba63
5 changed files with 93 additions and 61 deletions

View File

@ -18,7 +18,6 @@ package dorkbox.notify;
import java.awt.Dialog; import java.awt.Dialog;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Frame; import java.awt.Frame;
import java.awt.Image;
import java.awt.Window; import java.awt.Window;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
@ -43,7 +42,7 @@ class AsDialog extends JDialog implements INotify {
// this is on the swing EDT // this is on the swing EDT
@SuppressWarnings("NumericCastThatLosesPrecision") @SuppressWarnings("NumericCastThatLosesPrecision")
AsDialog(final Notify notification, final Image image, final ImageIcon imageIcon, final Window container, final Theme theme) { AsDialog(final Notify notification, final ImageIcon image, final Window container, final Theme theme) {
super(container, Dialog.ModalityType.MODELESS); super(container, Dialog.ModalityType.MODELESS);
this.notification = notification; this.notification = notification;
@ -60,7 +59,7 @@ class AsDialog extends JDialog implements INotify {
setTitle(notification.title); setTitle(notification.title);
setResizable(false); setResizable(false);
NotifyCanvas notifyCanvas = new NotifyCanvas(notification, imageIcon, theme); NotifyCanvas notifyCanvas = new NotifyCanvas(notification, image, theme);
add(notifyCanvas); add(notifyCanvas);
look = new LookAndFeel(this, notifyCanvas, notification, image, container.getBounds()); look = new LookAndFeel(this, notifyCanvas, notification, image, container.getBounds());

View File

@ -18,7 +18,6 @@ package dorkbox.notify;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.GraphicsDevice; import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment; import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.MouseInfo; import java.awt.MouseInfo;
import java.awt.Point; import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
@ -42,7 +41,7 @@ class AsFrame extends JFrame implements INotify {
// this is on the swing EDT // this is on the swing EDT
@SuppressWarnings("NumericCastThatLosesPrecision") @SuppressWarnings("NumericCastThatLosesPrecision")
AsFrame(final Notify notification, final Image image, final ImageIcon imageIcon, final Theme theme) { AsFrame(final Notify notification, final ImageIcon image, final Theme theme) {
this.notification = notification; this.notification = notification;
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
@ -90,7 +89,7 @@ class AsFrame extends JFrame implements INotify {
.getBounds(); .getBounds();
NotifyCanvas notifyCanvas = new NotifyCanvas(notification, imageIcon, theme); NotifyCanvas notifyCanvas = new NotifyCanvas(notification, image, theme);
getContentPane().add(notifyCanvas); getContentPane().add(notifyCanvas);
look = new LookAndFeel(this, notifyCanvas, notification, image, bounds); look = new LookAndFeel(this, notifyCanvas, notification, image, bounds);

View File

@ -15,7 +15,6 @@
*/ */
package dorkbox.notify; package dorkbox.notify;
import java.awt.Image;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Window; import java.awt.Window;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
@ -25,6 +24,8 @@ import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import javax.swing.ImageIcon;
import dorkbox.tweenengine.BaseTween; import dorkbox.tweenengine.BaseTween;
import dorkbox.tweenengine.Tween; import dorkbox.tweenengine.Tween;
import dorkbox.tweenengine.TweenCallback; import dorkbox.tweenengine.TweenCallback;
@ -91,7 +92,7 @@ class LookAndFeel {
LookAndFeel(final Window parent, LookAndFeel(final Window parent,
final NotifyCanvas notifyCanvas, final NotifyCanvas notifyCanvas,
final Notify notification, final Notify notification,
final Image image, final ImageIcon image,
final Rectangle parentBounds) { final Rectangle parentBounds) {
this.parent = parent; this.parent = parent;
@ -123,7 +124,7 @@ class LookAndFeel {
anchorY = getAnchorY(position, parentBounds); anchorY = getAnchorY(position, parentBounds);
if (image != null) { if (image != null) {
parent.setIconImage(image); parent.setIconImage(image.getImage());
} }
else { else {
parent.setIconImage(SwingUtil.BLANK_ICON); parent.setIconImage(SwingUtil.BLANK_ICON);

View File

@ -21,6 +21,7 @@ import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -28,6 +29,7 @@ import javax.imageio.ImageIO;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
import dorkbox.util.ActionHandler; import dorkbox.util.ActionHandler;
import dorkbox.util.ImageUtil;
import dorkbox.util.LocationResolver; import dorkbox.util.LocationResolver;
import dorkbox.util.Property; import dorkbox.util.Property;
import dorkbox.util.SwingUtil; import dorkbox.util.SwingUtil;
@ -55,6 +57,12 @@ import dorkbox.util.Version;
public final public final
class Notify { class Notify {
public static final String DIALOG_CONFIRM = "dialog-confirm.png";
public static final String DIALOG_INFORMATION = "dialog-information.png";
public static final String DIALOG_WARNING = "dialog-warning.png";
public static final String DIALOG_ERROR = "dialog-error.png";
/** /**
* This is the title font used by a notification. * This is the title font used by a notification.
*/ */
@ -79,8 +87,7 @@ class Notify {
@Property @Property
public static String IMAGE_PATH = "resources"; public static String IMAGE_PATH = "resources";
private static Map<String, BufferedImage> imageCache = new HashMap<String, BufferedImage>(4); private static Map<String, SoftReference<ImageIcon>> imageCache = new HashMap<String, SoftReference<ImageIcon>>(4);
private static Map<String, ImageIcon> imageIconCache = new HashMap<String, ImageIcon>(4);
/** /**
* Gets the version number. * Gets the version number.
@ -98,38 +105,69 @@ class Notify {
return new Notify(); return new Notify();
} }
/**
* Gets the size of the image to be used in the notification, which is a 48x48 pixel image.
*/
public static
int getImageSize() {
return 48;
}
/** /**
* Permits one to override the default images for the dialogs. This is NOT thread safe, and must be performed BEFORE showing a * Permits one to override the default images for the dialogs. This is NOT thread safe, and must be performed BEFORE showing a
* notification. * notification.
* <p> * <p>
* The image names are as follows: * The image names are as follows:
* <p> * <p>
* 'dialog-confirm.png' 'dialog-error.png' 'dialog-information.png' 'dialog-warning.png' * 'Notify.DIALOG_CONFIRM' 'Notify.DIALOG_INFORMATION' 'Notify.DIALOG_WARNING' 'Notify.DIALOG_ERROR'
* *
* @param imageName the name of the image, either your own if you want want it cached, or one of the above. * @param imageName the name of the image, either your own if you want want it cached, or one of the above.
* @param image the BufferedImage that you want to cache. * @param image the BufferedImage that you want to cache.
*/ */
public static public static
void setImagePath(String imageName, BufferedImage image) { void overrideDefaultImage(String imageName, BufferedImage image) {
if (imageCache.containsKey(imageName)) { if (imageCache.containsKey(imageName)) {
throw new RuntimeException("Unable to set an image that already has been set. This action must be done as soon as possible."); throw new RuntimeException("Unable to set an image that already has been set. This action must be done as soon as possible.");
} }
imageCache.put(imageName, image); Image imageImmediate = ImageUtil.getImageImmediate(image);
// we only use 48x48 pixel images. Resize as necessary
int width = imageImmediate.getWidth(null);
int height = imageImmediate.getHeight(null);
BufferedImage bufferedImage;
// resize the image, keep aspect ratio
if (width > height) {
bufferedImage = ImageUtil.resizeImage(image, getImageSize(), -1);
}
else {
bufferedImage = ImageUtil.resizeImage(image, -1, getImageSize());
}
imageCache.put(imageName, new SoftReference<ImageIcon>(new ImageIcon(bufferedImage)));
} }
private static private static
BufferedImage getImage(String imageName) { ImageIcon getImage(String imageName) {
BufferedImage bufferedImage = imageCache.get(imageName); ImageIcon image = null;
InputStream resourceAsStream = null; InputStream resourceAsStream = null;
try { try {
if (bufferedImage == null) { SoftReference<ImageIcon> reference = imageCache.get(imageName);
if (reference != null) {
image = reference.get();
}
if (image == null) {
String name = IMAGE_PATH + File.separatorChar + imageName; String name = IMAGE_PATH + File.separatorChar + imageName;
resourceAsStream = LocationResolver.getResourceAsStream(name); resourceAsStream = LocationResolver.getResourceAsStream(name);
bufferedImage = ImageIO.read(resourceAsStream); image = new ImageIcon(ImageUtil.getImageImmediate(ImageIO.read(resourceAsStream)));
imageCache.put(imageName, bufferedImage); imageCache.put(imageName, new SoftReference<ImageIcon>(image));
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -143,7 +181,7 @@ class Notify {
} }
} }
return bufferedImage; return image;
} }
@ -158,7 +196,7 @@ class Notify {
boolean hideCloseButton; boolean hideCloseButton;
boolean isDark = false; boolean isDark = false;
int screenNumber = Short.MIN_VALUE; int screenNumber = Short.MIN_VALUE;
private Image graphic; private ImageIcon icon;
ActionHandler<Notify> onCloseAction; ActionHandler<Notify> onCloseAction;
private INotify notifyPopup; private INotify notifyPopup;
@ -191,11 +229,24 @@ class Notify {
} }
/** /**
* Specifies the graphic * Specifies the image
*/ */
public public
Notify graphic(Image graphic) { Notify image(Image image) {
this.graphic = graphic; // we only use 48x48 pixel images. Resize as necessary
int width = image.getWidth(null);
int height = image.getHeight(null);
BufferedImage bufferedImage = ImageUtil.getBufferedImage(image);
// resize the image, keep aspect ratio
if (width > height) {
bufferedImage = ImageUtil.resizeImage(bufferedImage, 48, -1);
} else {
bufferedImage = ImageUtil.resizeImage(bufferedImage, -1, 48);
}
this.icon = new ImageIcon(bufferedImage);
return this; return this;
} }
@ -259,47 +310,48 @@ class Notify {
} }
/** /**
* Shows the notification with the built-in 'warning' graphic. * Shows the notification with the built-in 'warning' image.
*/ */
public public
void showWarning() { void showWarning() {
name = "dialog-warning.png"; name = DIALOG_WARNING;
graphic(getImage(name)); icon = getImage(name);
show(); show();
} }
/** /**
* Shows the notification with the built-in 'information' graphic. * Shows the notification with the built-in 'information' image.
*/ */
public public
void showInformation() { void showInformation() {
name = "dialog-information.png"; name = DIALOG_INFORMATION;
graphic(getImage(name)); icon = getImage(name);
show(); show();
} }
/** /**
* Shows the notification with the built-in 'error' graphic. * Shows the notification with the built-in 'error' image.
*/ */
public public
void showError() { void showError() {
name = "dialog-error.png"; name = DIALOG_ERROR;
graphic(getImage(name)); icon = getImage(name);
show(); show();
} }
/** /**
* Shows the notification with the built-in 'confirm' graphic. * Shows the notification with the built-in 'confirm' image.
*/ */
public public
void showConfirm() { void showConfirm() {
name = "dialog-confirm.png"; name = DIALOG_CONFIRM;
graphic(getImage(name)); icon = getImage(name);
show(); show();
} }
/** /**
* Shows the notification. If the Notification is assigned to a screen, but shown in a JFrame, the screen number will be ignored. * Shows the notification. If the Notification is assigned to a screen, but shown inside a Swing/etc parent, the screen number will be
* ignored.
*/ */
public public
void show() { void show() {
@ -310,26 +362,7 @@ class Notify {
public public
void run() { void run() {
final Notify notify = Notify.this; final Notify notify = Notify.this;
final Image graphic = notify.graphic; final ImageIcon image = notify.icon;
// we ONLY cache our own icons
ImageIcon imageIcon = null;
if (graphic != null) {
if (name != null) {
imageIcon = imageIconCache.get(name);
if (imageIcon == null) {
Image image = new ImageIcon(graphic).getImage();
// have to do this twice, so that it will finish loading the image (weird callback stuff is required if we don't do this)
imageIcon = new ImageIcon(image);
imageIconCache.put(name, imageIcon);
}
}
else {
imageIcon = new ImageIcon(graphic);
}
}
Theme theme; Theme theme;
if (notify.theme != null) { if (notify.theme != null) {
@ -340,9 +373,9 @@ class Notify {
} }
if (window == null) { if (window == null) {
notifyPopup = new AsFrame(notify, graphic, imageIcon, theme); notifyPopup = new AsFrame(notify, image, theme);
} else { } else {
notifyPopup = new AsDialog(notify, graphic, imageIcon, window, theme); notifyPopup = new AsDialog(notify, image, window, theme);
} }
notifyPopup.setVisible(true); notifyPopup.setVisible(true);
@ -354,7 +387,7 @@ class Notify {
}); });
// don't need to hang onto these. // don't need to hang onto these.
graphic = null; icon = null;
} }
/** /**

View File

@ -126,7 +126,7 @@ class NotifyTest {
.hideAfter(13000) .hideAfter(13000)
.position(Pos.BOTTOM_LEFT) .position(Pos.BOTTOM_LEFT)
// .setScreen(0) // .setScreen(0)
.darkStyle() // .darkStyle()
// .shake(1300, 4) // .shake(1300, 4)
// .shake(1300, 10) // .shake(1300, 10)
.hideCloseButton() .hideCloseButton()