diff --git a/Growl.iml b/Growl.iml
index 65470aa..5aef054 100644
--- a/Growl.iml
+++ b/Growl.iml
@@ -4,6 +4,8 @@
+
+
diff --git a/src/dorkbox/util/growl/Growl.java b/src/dorkbox/util/growl/Growl.java
index 7276510..ce52050 100644
--- a/src/dorkbox/util/growl/Growl.java
+++ b/src/dorkbox/util/growl/Growl.java
@@ -49,8 +49,6 @@ import java.util.Map;
public final
class Growl {
- public static final int FOREVER = 0;
-
/**
* Location of the dialog image resources. By default they must be in the 'resources' directory relative to the application
*/
@@ -119,7 +117,7 @@ class Growl {
String title;
String text;
Pos position = Pos.BOTTOM_RIGHT;
- int hideAfterDurationInMillis = 5000;
+ int hideAfterDurationInMillis = 0;
boolean hideCloseButton;
boolean isDark = false;
int screenNumber = Short.MIN_VALUE;
@@ -171,7 +169,8 @@ class Growl {
}
/**
- * Specifies the duration that the notification should show, after which it will be hidden. 0 means to show forever.
+ * Specifies the duration that the notification should show, after which it will be hidden. 0 means to show forever. By default it
+ * will show forever
*/
public
Growl hideAfter(int durationInMillis) {
@@ -294,10 +293,13 @@ class Growl {
}
/**
- * "shakes" the notification, to bring user attention
+ * "shakes" the notification, to bring user attention to it.
+ *
+ * @param durationInMillis now long it will shake
+ * @param amplitude a measure of how much it needs to shake. 4 is a small amount of shaking, 10 is a lot.
*/
public
- void shake(final int durationInMillis, final int amplitude) {
+ Growl shake(final int durationInMillis, final int amplitude) {
this.shakeDurationInMillis = durationInMillis;
this.shakeAmplitude = amplitude;
@@ -312,6 +314,8 @@ class Growl {
}
});
}
+
+ return this;
}
/**
diff --git a/src/dorkbox/util/growl/GrowlPopup.java b/src/dorkbox/util/growl/GrowlPopup.java
index c21ecd2..73949b4 100644
--- a/src/dorkbox/util/growl/GrowlPopup.java
+++ b/src/dorkbox/util/growl/GrowlPopup.java
@@ -15,67 +15,68 @@
*/
package dorkbox.util.growl;
+import dorkbox.util.ActionHandlerLong;
import dorkbox.util.OS;
+import dorkbox.util.Property;
import dorkbox.util.ScreenUtil;
import dorkbox.util.SwingUtil;
-import dorkbox.util.SystemProps;
+import dorkbox.util.swing.SwingActiveRender;
import dorkbox.util.tweenengine.BaseTween;
import dorkbox.util.tweenengine.Tween;
import dorkbox.util.tweenengine.TweenCallback;
import dorkbox.util.tweenengine.TweenEquations;
import dorkbox.util.tweenengine.TweenManager;
-import javax.swing.Box;
-import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.SwingConstants;
-import javax.swing.Timer;
-import javax.swing.border.EmptyBorder;
import java.awt.*;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
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;
// 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")
+@SuppressWarnings({"Duplicates", "FieldCanBeLocal"})
public
class GrowlPopup extends JFrame {
- private static final int padding = 40;
+ @Property
+ /** title font used by growl */
+ public static String TITLE_TEXT_FONT = "Source Code Pro BOLD 16";
- public static final float FADE_DURATION = 1.5F;
+ @Property
+ /** main text font used by growl */
+ 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 final float MOVE_DURATION = 1.0F;
+ private static final int padding = 40;
+
private static final Map> popups = new HashMap>();
private static final GrowlPopupAccessor accessor = new GrowlPopupAccessor();
private static final TweenManager tweenManager = new TweenManager();
+ private static ActionHandlerLong frameStartHandler;
- private static Timer timer;
private static WindowUtil opacity_compat;
-
static {
- // this timer is on the EDT (this is not the java.util timer)
- // 30 times a second
- //noinspection Convert2Lambda
- timer = new Timer(1000/30, new ActionListener() {
+ // this is for updating the tween engine during active-rendering
+ frameStartHandler = new ActionHandlerLong() {
@Override
public
- void actionPerformed(final ActionEvent e) {
- tweenManager.update();
+ void handle(final long deltaInNanos) {
+ GrowlPopup.tweenManager.update(deltaInNanos);
}
- });
- timer.setRepeats(true);
+ };
if (OS.javaVersion == 6) {
opacity_compat = new WindowUtil_Java6();
@@ -85,8 +86,23 @@ class GrowlPopup extends JFrame {
}
private static final int WIDTH = 300;
- private static final int HEIGHT = 90;
+ 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;
@@ -96,6 +112,7 @@ class GrowlPopup extends JFrame {
private final MouseAdapter mouseListener;
private final Growl 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;
@@ -105,11 +122,21 @@ class GrowlPopup extends JFrame {
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")
GrowlPopup(Growl notification, Image image, ImageIcon imageIcon) {
this.notification = notification;
+ this.imageIcon = imageIcon;
windowListener = new GrowlPopupWindowAdapter();
mouseListener = new GrowlPopupClickAdapter();
@@ -117,7 +144,9 @@ class GrowlPopup extends JFrame {
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);
@@ -136,26 +165,23 @@ class GrowlPopup extends JFrame {
addMouseListener(mouseListener);
- final Color text_BG;
- final Color titleText_FG;
- final Color mainText_FG;
- final Color closeX_FG;
-
if (notification.isDark) {
- text_BG = Color.DARK_GRAY;
+ panel_BG = Color.DARK_GRAY;
titleText_FG = Color.GRAY;
mainText_FG = Color.LIGHT_GRAY;
closeX_FG = Color.GRAY;
+ progress_FG = Color.gray;
}
else {
- text_BG = Color.WHITE;
- titleText_FG = Color.DARK_GRAY;
+ panel_BG = Color.WHITE;
+ titleText_FG = Color.GRAY.darker();
mainText_FG = Color.GRAY;
closeX_FG = Color.LIGHT_GRAY;
+ progress_FG = new Color(0x42A5F5);
}
- setBackground(text_BG);
-
+ setBackground(panel_BG);
+ showCloseButton = !notification.hideCloseButton;
GraphicsDevice device;
if (notification.screenNumber == Short.MIN_VALUE) {
@@ -192,105 +218,6 @@ class GrowlPopup extends JFrame {
final int screenWidth = (int) screenBounds.getWidth();
final int screenHeight = (int) screenBounds.getHeight();
- // makes sure everything is spaced nicely
- final JPanel contentPane = new JPanel();
- contentPane.setBackground(text_BG);
- contentPane.setBorder(new EmptyBorder(0, 10, 10, 5));
- contentPane.setLayout(new BorderLayout(10, 5));
- setContentPane(contentPane);
-
-
- // closebutton is the 'x' button, but it is really just a font
- Font closeButtonFont = SwingUtil.getFontFromProperty(SystemProps.growl_closeButtonFontName, "Source Code Pro", Font.BOLD, 12);
- Font mainTextFont = SwingUtil.getFontFromProperty(SystemProps.growl_titleTextFontName, "Source Code Pro", Font.BOLD, 14);
- Font titleTextFont = SwingUtil.getFontFromProperty(SystemProps.growl_mainTextFontName, "Source Code Pro", Font.BOLD, 16);
-
- // TITLE AND CLOSE BUTTON
- {
- Box box = new Box(BoxLayout.X_AXIS);
- box.setBackground(text_BG);
-
- box.setAlignmentX(Component.CENTER_ALIGNMENT);
-
- {
- Box textBox = new Box(BoxLayout.X_AXIS);
- textBox.setAlignmentX(Component.LEFT_ALIGNMENT);
-
- final JLabel titleLabel = new JLabel();
- titleLabel.setForeground(titleText_FG);
- titleLabel.setFont(titleTextFont);
- titleLabel.setText(notification.title);
-
- textBox.add(titleLabel);
- textBox.add(Box.createHorizontalGlue());
-
- box.add(textBox);
-
- if (!notification.hideCloseButton) {
- // can specify to hide the close button
- Box closeBox = new Box(BoxLayout.X_AXIS);
- closeBox.setBorder(new EmptyBorder(4, 4, 4, 4));
- closeBox.setAlignmentX(Component.RIGHT_ALIGNMENT);
-
- final JLabel closeButton = new JLabel();
- closeButton.setForeground(closeX_FG);
- closeButton.setFont(closeButtonFont);
- closeButton.setText("x");
- closeButton.setVerticalTextPosition(SwingConstants.TOP);
-
- closeBox.addMouseListener(new GrowlCloseAdapter(this));
- closeBox.add(closeButton);
-
- box.add(closeBox);
- }
- }
-
- contentPane.add(box, BorderLayout.NORTH);
- }
-
-
- int textLengthLimit = 98;
-
- // ICON
- if (imageIcon != null) {
- textLengthLimit = 76;
- JLabel iconLabel = new JLabel(imageIcon);
- contentPane.add(iconLabel, BorderLayout.WEST);
- }
-
- // MAIN TEXT
- {
- 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, "