Added support for notifications on a JFrame/Window. Refactored
LookAndFeel of the notification icon. Fixed various bugs surrounding animation and rendering of notifications.
This commit is contained in:
parent
04cfd7d91b
commit
f8866c9aeb
170
src/dorkbox/notify/AsDialog.java
Normal file
170
src/dorkbox/notify/AsDialog.java
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* 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.Dialog;
|
||||||
|
import java.awt.Frame;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.Window;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
import java.awt.event.WindowStateListener;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
|
||||||
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
|
// this is a child to a Jframe/window (instead of globally to the screen)
|
||||||
|
@SuppressWarnings({"Duplicates", "FieldCanBeLocal", "WeakerAccess", "DanglingJavadoc"})
|
||||||
|
public
|
||||||
|
class AsDialog extends JDialog implements INotify {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final LookAndFeel look;
|
||||||
|
private final Notify notification;
|
||||||
|
private final ComponentListener parentListener;
|
||||||
|
|
||||||
|
// this is on the swing EDT
|
||||||
|
@SuppressWarnings("NumericCastThatLosesPrecision")
|
||||||
|
AsDialog(final Notify notification, final Image image, final ImageIcon imageIcon, final Window container) {
|
||||||
|
super(container, Dialog.ModalityType.MODELESS);
|
||||||
|
this.notification = notification;
|
||||||
|
|
||||||
|
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||||
|
setUndecorated(true);
|
||||||
|
setLayout(null);
|
||||||
|
|
||||||
|
setSize(LookAndFeel.WIDTH, LookAndFeel.HEIGHT);
|
||||||
|
setLocation(Short.MIN_VALUE, Short.MIN_VALUE);
|
||||||
|
|
||||||
|
setTitle(notification.title);
|
||||||
|
setResizable(false);
|
||||||
|
|
||||||
|
look = new LookAndFeel(this, notification, image, imageIcon, container.getBounds());
|
||||||
|
|
||||||
|
parentListener = new ComponentListener() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void componentShown(final ComponentEvent e) {
|
||||||
|
AsDialog.this.setVisible(true);
|
||||||
|
look.reLayout(container.getBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void componentHidden(final ComponentEvent e) {
|
||||||
|
AsDialog.this.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void componentResized(final ComponentEvent e) {
|
||||||
|
look.reLayout(container.getBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void componentMoved(final ComponentEvent e) {
|
||||||
|
look.reLayout(container.getBounds());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
container.addWindowStateListener(new WindowStateListener() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void windowStateChanged(WindowEvent e) {
|
||||||
|
int state = e.getNewState();
|
||||||
|
if ((state & Frame.ICONIFIED) != 0) {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setVisible(true);
|
||||||
|
look.reLayout(container.getBounds());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
container.addComponentListener(parentListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void paint(Graphics g) {
|
||||||
|
look.paint(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void onClick(final int x, final int y) {
|
||||||
|
look.onClick(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shakes the popup
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void shake(final int durationInMillis, final int amplitude) {
|
||||||
|
look.shake(durationInMillis, amplitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setVisible(final boolean b) {
|
||||||
|
// was it already visible?
|
||||||
|
if (b == isVisible()) {
|
||||||
|
// prevent "double setting" visible state
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setVisible(b);
|
||||||
|
look.setVisible(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void close() {
|
||||||
|
// this must happen in the Swing EDT. This is usually called by the active renderer
|
||||||
|
SwingUtil.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
// set it off screen (which is what the close method also does)
|
||||||
|
if (isVisible()) {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
look.close();
|
||||||
|
|
||||||
|
if (parentListener != null) {
|
||||||
|
removeComponentListener(parentListener);
|
||||||
|
}
|
||||||
|
setIconImage(null);
|
||||||
|
removeAll();
|
||||||
|
dispose();
|
||||||
|
|
||||||
|
notification.onClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
151
src/dorkbox/notify/AsFrame.java
Normal file
151
src/dorkbox/notify/AsFrame.java
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* 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.Graphics;
|
||||||
|
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 javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
|
||||||
|
import dorkbox.util.ScreenUtil;
|
||||||
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
|
// 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", "WeakerAccess", "DanglingJavadoc"})
|
||||||
|
public
|
||||||
|
class AsFrame extends JFrame implements INotify {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final LookAndFeel look;
|
||||||
|
private final Notify notification;
|
||||||
|
|
||||||
|
|
||||||
|
// this is on the swing EDT
|
||||||
|
@SuppressWarnings("NumericCastThatLosesPrecision")
|
||||||
|
AsFrame(final Notify notification, final Image image, final ImageIcon imageIcon) {
|
||||||
|
this.notification = notification;
|
||||||
|
|
||||||
|
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||||
|
setUndecorated(true);
|
||||||
|
setAlwaysOnTop(true);
|
||||||
|
setLayout(null);
|
||||||
|
|
||||||
|
setSize(LookAndFeel.WIDTH, LookAndFeel.HEIGHT);
|
||||||
|
setLocation(Short.MIN_VALUE, Short.MIN_VALUE);
|
||||||
|
|
||||||
|
setTitle(notification.title);
|
||||||
|
setResizable(false);
|
||||||
|
|
||||||
|
Rectangle bounds;
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
bounds = device.getDefaultConfiguration()
|
||||||
|
.getBounds();
|
||||||
|
|
||||||
|
look = new LookAndFeel(this, notification, image, imageIcon, bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void paint(Graphics g) {
|
||||||
|
look.paint(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void onClick(final int x, final int y) {
|
||||||
|
look.onClick(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shakes the popup
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void shake(final int durationInMillis, final int amplitude) {
|
||||||
|
look.shake(durationInMillis, amplitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setVisible(final boolean b) {
|
||||||
|
// was it already visible?
|
||||||
|
if (b == isVisible()) {
|
||||||
|
// prevent "double setting" visible state
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setVisible(b);
|
||||||
|
look.setVisible(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void close() {
|
||||||
|
// this must happen in the Swing EDT. This is usually called by the active renderer
|
||||||
|
SwingUtil.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
// set it off screen (which is what the close method also does)
|
||||||
|
if (isVisible()) {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
look.close();
|
||||||
|
|
||||||
|
setIconImage(null);
|
||||||
|
removeAll();
|
||||||
|
dispose();
|
||||||
|
|
||||||
|
notification.onClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,16 +18,15 @@ package dorkbox.notify;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
|
||||||
class NotifyPopupClickAdapter extends MouseAdapter {
|
class ClickAdapter extends MouseAdapter {
|
||||||
|
|
||||||
public
|
ClickAdapter() {
|
||||||
NotifyPopupClickAdapter() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void mouseReleased(final MouseEvent e) {
|
void mouseReleased(final MouseEvent e) {
|
||||||
NotifyPopup source = (NotifyPopup) e.getSource();
|
INotify source = (INotify) e.getSource();
|
||||||
source.onClick(e.getX(), e.getY());
|
source.onClick(e.getX(), e.getY());
|
||||||
}
|
}
|
||||||
}
|
}
|
27
src/dorkbox/notify/INotify.java
Normal file
27
src/dorkbox/notify/INotify.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
public
|
||||||
|
interface INotify {
|
||||||
|
void close();
|
||||||
|
|
||||||
|
void shake(int durationInMillis, int amplitude);
|
||||||
|
|
||||||
|
void setVisible(boolean b);
|
||||||
|
|
||||||
|
void onClick(int x, int y);
|
||||||
|
}
|
|
@ -19,16 +19,13 @@ import java.awt.BasicStroke;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.GraphicsDevice;
|
|
||||||
import java.awt.GraphicsEnvironment;
|
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.MouseInfo;
|
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.Stroke;
|
import java.awt.Stroke;
|
||||||
|
import java.awt.Window;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.WindowAdapter;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -37,7 +34,6 @@ import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JFrame;
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
|
|
||||||
import dorkbox.tweenengine.BaseTween;
|
import dorkbox.tweenengine.BaseTween;
|
||||||
|
@ -48,52 +44,21 @@ import dorkbox.tweenengine.TweenManager;
|
||||||
import dorkbox.util.ActionHandler;
|
import dorkbox.util.ActionHandler;
|
||||||
import dorkbox.util.ActionHandlerLong;
|
import dorkbox.util.ActionHandlerLong;
|
||||||
import dorkbox.util.FontUtil;
|
import dorkbox.util.FontUtil;
|
||||||
import dorkbox.util.Property;
|
|
||||||
import dorkbox.util.ScreenUtil;
|
|
||||||
import dorkbox.util.SwingUtil;
|
import dorkbox.util.SwingUtil;
|
||||||
import dorkbox.util.swing.SwingActiveRender;
|
import dorkbox.util.swing.SwingActiveRender;
|
||||||
|
|
||||||
// we can't use regular popup, because if we have no owner, it won't work!
|
@SuppressWarnings({"FieldCanBeLocal"})
|
||||||
// instead, we just create a JFrame and use it to hold our content
|
class LookAndFeel {
|
||||||
@SuppressWarnings({"Duplicates", "FieldCanBeLocal", "WeakerAccess", "DanglingJavadoc"})
|
private static final Map<String, ArrayList<LookAndFeel>> popups = new HashMap<String, ArrayList<LookAndFeel>>();
|
||||||
public
|
|
||||||
class NotifyPopup extends JFrame {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Property
|
static final NotifyAccessor accessor = new NotifyAccessor();
|
||||||
/** This is the title font used by a notification. */
|
static final TweenManager tweenManager = new TweenManager();
|
||||||
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<String, ArrayList<NotifyPopup>> popups = new HashMap<String, ArrayList<NotifyPopup>>();
|
|
||||||
|
|
||||||
private static final NotifyPopupAccessor accessor = new NotifyPopupAccessor();
|
|
||||||
private static final TweenManager tweenManager = new TweenManager();
|
|
||||||
private static final ActionHandlerLong frameStartHandler;
|
private static final ActionHandlerLong frameStartHandler;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int WIDTH = 300;
|
|
||||||
private static final int HEIGHT = 87;
|
private static final java.awt.event.WindowAdapter windowListener = new WindowAdapter();
|
||||||
private static final int PROGRESS_HEIGHT = HEIGHT - 1;
|
private static final MouseAdapter mouseListener = new ClickAdapter();
|
||||||
|
|
||||||
private static final Stroke stroke = new BasicStroke(2);
|
private static final Stroke stroke = new BasicStroke(2);
|
||||||
private static final int closeX = 282;
|
private static final int closeX = 282;
|
||||||
|
@ -104,28 +69,46 @@ class NotifyPopup extends JFrame {
|
||||||
private static final int Y_2 = closeY + 11;
|
private static final int Y_2 = closeY + 11;
|
||||||
private static final int X_2 = closeX + 11;
|
private static final int X_2 = closeX + 11;
|
||||||
|
|
||||||
|
static final int WIDTH = 300;
|
||||||
|
static final int HEIGHT = 87;
|
||||||
|
private static final int PROGRESS_HEIGHT = HEIGHT - 2;
|
||||||
|
|
||||||
|
private static final int PADDING = 40;
|
||||||
|
|
||||||
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
|
private static final float MOVE_DURATION = Notify.MOVE_DURATION;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// this is for updating the tween engine during active-rendering
|
||||||
|
frameStartHandler = new ActionHandlerLong() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void handle(final long deltaInNanos) {
|
||||||
|
LookAndFeel.tweenManager.update(deltaInNanos);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private volatile int anchorX;
|
||||||
|
private volatile int anchorY;
|
||||||
|
|
||||||
private final Color panel_BG;
|
private final Color panel_BG;
|
||||||
private final Color titleText_FG;
|
private final Color titleText_FG;
|
||||||
private final Color mainText_FG;
|
private final Color mainText_FG;
|
||||||
private final Color closeX_FG;
|
private final Color closeX_FG;
|
||||||
private final Color progress_FG;
|
private final Color progress_FG;
|
||||||
|
|
||||||
|
private final boolean showCloseButton;
|
||||||
|
private final BufferedImage cachedImage;
|
||||||
|
|
||||||
private final int anchorX;
|
private final Window parent;
|
||||||
private final int anchorY;
|
|
||||||
|
|
||||||
private static final WindowAdapter windowListener = new NotifyPopupWindowAdapter();
|
|
||||||
private static final MouseAdapter mouseListener = new NotifyPopupClickAdapter();
|
|
||||||
|
|
||||||
private final Notify notification;
|
|
||||||
|
|
||||||
private final float hideAfterDurationInSeconds;
|
private final float hideAfterDurationInSeconds;
|
||||||
private final Pos position;
|
private final Pos position;
|
||||||
private final ActionHandler<Notify> onCloseAction;
|
|
||||||
|
|
||||||
// this is used in combination with position, so that we can track which screen and what position a popup is in
|
// 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 final String idAndPosition;
|
||||||
|
|
||||||
private int popupIndex;
|
private int popupIndex;
|
||||||
|
|
||||||
private volatile Tween tween = null;
|
private volatile Tween tween = null;
|
||||||
|
@ -135,39 +118,13 @@ class NotifyPopup extends JFrame {
|
||||||
// non-volatile because it's always accessed in the active render thread
|
// non-volatile because it's always accessed in the active render thread
|
||||||
private int progress = 0;
|
private int progress = 0;
|
||||||
|
|
||||||
private final boolean showCloseButton;
|
private final ActionHandler<Notify> onCloseAction;
|
||||||
private final BufferedImage cachedImage;
|
|
||||||
private static final Random RANDOM = new Random();
|
|
||||||
|
|
||||||
|
LookAndFeel(final Window parent, final Notify notification, final Image image, final ImageIcon imageIcon, final Rectangle parentBounds) {
|
||||||
|
this.parent = parent;
|
||||||
|
|
||||||
|
parent.addWindowListener(windowListener);
|
||||||
// this is on the swing EDT
|
parent.addMouseListener(mouseListener);
|
||||||
@SuppressWarnings("NumericCastThatLosesPrecision")
|
|
||||||
NotifyPopup(final Notify notification, final Image image, final ImageIcon imageIcon) {
|
|
||||||
this.notification = notification;
|
|
||||||
|
|
||||||
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
|
||||||
setUndecorated(true);
|
|
||||||
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) {
|
if (notification.isDark) {
|
||||||
panel_BG = Color.DARK_GRAY;
|
panel_BG = Color.DARK_GRAY;
|
||||||
|
@ -184,11 +141,15 @@ class NotifyPopup extends JFrame {
|
||||||
progress_FG = new Color(0x42A5F5);
|
progress_FG = new Color(0x42A5F5);
|
||||||
}
|
}
|
||||||
|
|
||||||
setBackground(panel_BG);
|
|
||||||
showCloseButton = !notification.hideCloseButton;
|
|
||||||
hideAfterDurationInSeconds = notification.hideAfterDurationInMillis / 1000.0F;
|
hideAfterDurationInSeconds = notification.hideAfterDurationInMillis / 1000.0F;
|
||||||
position = notification.position;
|
position = notification.position;
|
||||||
|
|
||||||
|
showCloseButton = !notification.hideCloseButton;
|
||||||
|
|
||||||
|
// now we setup the rendering of the image
|
||||||
|
cachedImage = renderBackgroundInfo(notification.title, notification.text, titleText_FG, mainText_FG, panel_BG, imageIcon);
|
||||||
|
|
||||||
|
|
||||||
if (notification.onCloseAction != null) {
|
if (notification.onCloseAction != null) {
|
||||||
onCloseAction = new ActionHandler<Notify>() {
|
onCloseAction = new ActionHandler<Notify>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -197,91 +158,229 @@ class NotifyPopup extends JFrame {
|
||||||
notification.onCloseAction.handle(notification);
|
notification.onCloseAction.handle(notification);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
onCloseAction = null;
|
onCloseAction = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsDevice device;
|
idAndPosition = parentBounds.x + ":" + parentBounds.y + ":" + parentBounds.width + ":" + parentBounds.height + ":" + position;
|
||||||
if (notification.screenNumber == Short.MIN_VALUE) {
|
|
||||||
// set screen position based on mouse
|
|
||||||
Point mouseLocation = MouseInfo.getPointerInfo()
|
|
||||||
.getLocation();
|
|
||||||
|
|
||||||
device = ScreenUtil.getGraphicsDeviceAt(mouseLocation);
|
anchorX = getAnchorX(position, parentBounds);
|
||||||
|
anchorY = getAnchorY(position, parentBounds);
|
||||||
|
|
||||||
|
parent.setBackground(panel_BG);
|
||||||
|
if (image != null) {
|
||||||
|
parent.setIconImage(image);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// set screen position based on specified screen
|
parent.setIconImage(SwingUtil.BLANK_ICON);
|
||||||
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];
|
void paint(final Graphics g) {
|
||||||
|
// we cache the text + image (to another image), and then always render the close + progressbar
|
||||||
|
|
||||||
|
// use our cached image, so we don't have to re-render text/background/etc
|
||||||
|
g.drawImage(cachedImage, 0, 0, null);
|
||||||
|
|
||||||
|
// the progress bar and close button are the only things that can change, so we always draw them every time
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
try {
|
||||||
|
if (showCloseButton) {
|
||||||
|
Graphics2D g3 = (Graphics2D) g.create();
|
||||||
|
|
||||||
|
g3.setColor(panel_BG);
|
||||||
|
g3.setStroke(stroke);
|
||||||
|
|
||||||
|
final Point p = parent.getMousePosition();
|
||||||
|
// reasonable position for detecting mouse over
|
||||||
|
if (p != null && p.getX() >= 280 && p.getY() <= 20) {
|
||||||
|
g3.setColor(Color.RED);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g3.setColor(closeX_FG);
|
||||||
}
|
}
|
||||||
|
|
||||||
idAndPosition = device.getIDstring() + notification.position;
|
// draw the X
|
||||||
|
g3.drawLine(X_1, Y_1, X_2, Y_2);
|
||||||
|
g3.drawLine(X_2, Y_1, X_1, Y_2);
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle screenBounds = device.getDefaultConfiguration()
|
g2.setColor(progress_FG);
|
||||||
.getBounds();
|
g2.fillRect(0, PROGRESS_HEIGHT, progress, 2);
|
||||||
|
} finally {
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
if (hideTween != null) {
|
||||||
|
hideTween.cancel();
|
||||||
|
hideTween = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tween != null) {
|
||||||
|
tween.cancel();
|
||||||
|
tween = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.removeWindowListener(windowListener);
|
||||||
|
parent.removeMouseListener(mouseListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onClick(final int x, final int y) {
|
||||||
|
// Check - we were over the 'X' (and thus no notify), or was it in the general area?
|
||||||
|
|
||||||
|
if (showCloseButton && x >= 280 && y <= 20) {
|
||||||
|
// reasonable position for detecting mouse over
|
||||||
|
((INotify)parent).close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (onCloseAction != null) {
|
||||||
|
onCloseAction.handle(null);
|
||||||
|
}
|
||||||
|
((INotify) parent).close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reLayout(final Rectangle bounds) {
|
||||||
|
// when the parent window moves, we stop all animation and snap the popup into place. This simplifies logic greatly
|
||||||
|
anchorX = getAnchorX(position, bounds);
|
||||||
|
anchorY = getAnchorY(position, bounds);
|
||||||
|
|
||||||
|
boolean showFromTop = isShowFromTop(this);
|
||||||
|
|
||||||
|
if (tween != null) {
|
||||||
|
tween.cancel(); // cancel does it's thing on the next tick of animation cycle
|
||||||
|
tween = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int changedY;
|
||||||
|
if (showFromTop) {
|
||||||
|
changedY = anchorY + (popupIndex * (HEIGHT + 10));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
changedY = anchorY - (popupIndex * (HEIGHT + 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.setLocation(anchorX, changedY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shake(final int durationInMillis, final int amplitude) {
|
||||||
|
int i1 = RANDOM.nextInt((amplitude << 2) + 1) - amplitude;
|
||||||
|
int i2 = RANDOM.nextInt((amplitude << 2) + 1) - amplitude;
|
||||||
|
|
||||||
|
i1 = i1 >> 2;
|
||||||
|
i2 = i2 >> 2;
|
||||||
|
|
||||||
|
// make sure it always moves by some amount
|
||||||
|
if (i1 < 0) {
|
||||||
|
i1 -= amplitude >> 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i1 += amplitude >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2 < 0) {
|
||||||
|
i2 -= amplitude >> 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
i2 += amplitude >> 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = durationInMillis / 50;
|
||||||
|
// make sure we always end the animation where we start
|
||||||
|
if ((count & 1) == 0) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tween tween = Tween.to(this, NotifyAccessor.X_Y_POS, accessor, 0.05F)
|
||||||
|
.targetRelative(i1, i2)
|
||||||
|
.repeatAutoReverse(count, 0)
|
||||||
|
.ease(TweenEquations.Linear);
|
||||||
|
tweenManager.add(tween);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setY(final int y) {
|
||||||
|
parent.setLocation(parent.getX(), y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProgress(final int progress) {
|
||||||
|
this.progress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getProgress() {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getY() {
|
||||||
|
return parent.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getX() {
|
||||||
|
return parent.getX();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVisible(final boolean visible) {
|
||||||
|
if (visible) {
|
||||||
|
parent.toFront();
|
||||||
|
|
||||||
|
// set this jframe to use active rendering
|
||||||
|
SwingActiveRender.addActiveRender(parent);
|
||||||
|
addPopupToMap(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
removePopupFromMap(this);
|
||||||
|
SwingActiveRender.removeActiveRender(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
int getAnchorX(final Pos position, final Rectangle bounds) {
|
||||||
// we use the screen that the mouse is currently on.
|
// we use the screen that the mouse is currently on.
|
||||||
final int startX = (int) screenBounds.getX();
|
final int startX = (int) bounds.getX();
|
||||||
final int startY = (int) screenBounds.getY();
|
final int screenWidth = (int) bounds.getWidth();
|
||||||
final int screenWidth = (int) screenBounds.getWidth();
|
|
||||||
final int screenHeight = (int) screenBounds.getHeight();
|
|
||||||
|
|
||||||
|
|
||||||
// determine location for the popup
|
// determine location for the popup
|
||||||
final Pos position = notification.position;
|
|
||||||
|
|
||||||
// get anchorX
|
// get anchorX
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case TOP_LEFT:
|
case TOP_LEFT:
|
||||||
case BOTTOM_LEFT:
|
case BOTTOM_LEFT:
|
||||||
anchorX = startX + padding;
|
return startX + PADDING;
|
||||||
break;
|
|
||||||
|
|
||||||
case CENTER:
|
case CENTER:
|
||||||
anchorX = startX + (screenWidth / 2) - WIDTH / 2 - padding / 2;
|
return startX + (screenWidth / 2) - WIDTH / 2 - PADDING / 2;
|
||||||
break;
|
|
||||||
|
|
||||||
case TOP_RIGHT:
|
case TOP_RIGHT:
|
||||||
case BOTTOM_RIGHT:
|
case BOTTOM_RIGHT:
|
||||||
anchorX = startX + screenWidth - WIDTH - padding;
|
return startX + screenWidth - WIDTH - PADDING;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Unknown position. '" + position + "'");
|
throw new RuntimeException("Unknown position. '" + position + "'");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static
|
||||||
|
int getAnchorY(final Pos position, final Rectangle bounds) {
|
||||||
|
final int startY = (int) bounds.getY();
|
||||||
|
final int screenHeight = (int) bounds.getHeight();
|
||||||
|
|
||||||
// get anchorY
|
// get anchorY
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case TOP_LEFT:
|
case TOP_LEFT:
|
||||||
case TOP_RIGHT:
|
case TOP_RIGHT:
|
||||||
anchorY = padding + startY;
|
return PADDING + startY;
|
||||||
break;
|
|
||||||
|
|
||||||
case CENTER:
|
case CENTER:
|
||||||
anchorY = startY + (screenHeight / 2) - HEIGHT / 2 - padding / 2;
|
return startY + (screenHeight / 2) - HEIGHT / 2 - PADDING / 2;
|
||||||
break;
|
|
||||||
|
|
||||||
case BOTTOM_LEFT:
|
case BOTTOM_LEFT:
|
||||||
case BOTTOM_RIGHT:
|
case BOTTOM_RIGHT:
|
||||||
anchorY = startY + screenHeight - HEIGHT - padding;
|
return startY + screenHeight - HEIGHT - PADDING;
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Unknown position. '" + position + "'");
|
throw new RuntimeException("Unknown position. '" + position + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we setup the rendering of the image
|
|
||||||
cachedImage = renderBackgroundInfo(notification.title, notification.text, titleText_FG, mainText_FG, panel_BG, imageIcon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static
|
private static
|
||||||
|
@ -303,14 +402,12 @@ class NotifyPopup extends JFrame {
|
||||||
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||||
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
|
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
|
||||||
|
|
||||||
// g2.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
g2.setColor(panel_BG);
|
g2.setColor(panel_BG);
|
||||||
g2.fillRect(0, 0, WIDTH, HEIGHT);
|
g2.fillRect(0, 0, WIDTH, HEIGHT);
|
||||||
|
|
||||||
// Draw the title text
|
// Draw the title text
|
||||||
java.awt.Font titleTextFont = FontUtil.parseFont(TITLE_TEXT_FONT);
|
java.awt.Font titleTextFont = FontUtil.parseFont(Notify.TITLE_TEXT_FONT);
|
||||||
g2.setColor(titleText_FG);
|
g2.setColor(titleText_FG);
|
||||||
g2.setFont(titleTextFont);
|
g2.setFont(titleTextFont);
|
||||||
g2.drawString(title, 5, 20);
|
g2.drawString(title, 5, 20);
|
||||||
|
@ -329,12 +426,12 @@ class NotifyPopup extends JFrame {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the main text
|
// Draw the main text
|
||||||
java.awt.Font mainTextFont = FontUtil.parseFont(MAIN_TEXT_FONT);
|
java.awt.Font mainTextFont = FontUtil.parseFont(Notify.MAIN_TEXT_FONT);
|
||||||
int length = notificationText.length();
|
int length = notificationText.length();
|
||||||
StringBuilder text = new StringBuilder(length);
|
StringBuilder text = new StringBuilder(length);
|
||||||
|
|
||||||
// are we "html" already? just check for the starting tag and strip off END html tag
|
// are we "html" already? just check for the starting tag and strip off END html tag
|
||||||
if (length >= 13 && notificationText.regionMatches(true, length-7, "</html>", 0, 7)) {
|
if (length >= 13 && notificationText.regionMatches(true, length - 7, "</html>", 0, 7)) {
|
||||||
text.append(notificationText);
|
text.append(notificationText);
|
||||||
text.delete(text.length() - 7, text.length());
|
text.delete(text.length() - 7, text.length());
|
||||||
|
|
||||||
|
@ -368,148 +465,53 @@ class NotifyPopup extends JFrame {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void paint(Graphics g) {
|
|
||||||
// we cache the text + image (to another image), and then always render the close + progressbar
|
|
||||||
|
|
||||||
// use our cached image, so we don't have to re-render text/background/etc
|
|
||||||
g.drawImage(cachedImage, 0, 0, null);
|
|
||||||
|
|
||||||
// the progress bar and close button are the only things that can change, so we always draw them every time
|
|
||||||
Graphics2D g2 = (Graphics2D) g.create();
|
|
||||||
try {
|
|
||||||
if (showCloseButton) {
|
|
||||||
Graphics2D g3 = (Graphics2D) g.create();
|
|
||||||
|
|
||||||
g3.setColor(panel_BG);
|
|
||||||
g3.setStroke(stroke);
|
|
||||||
|
|
||||||
final Point p = getMousePosition();
|
|
||||||
// reasonable position for detecting mouse over
|
|
||||||
if (p != null && p.getX() >= 280 && p.getY() <= 20) {
|
|
||||||
g3.setColor(Color.RED);
|
|
||||||
} else {
|
|
||||||
g3.setColor(closeX_FG);
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw the X
|
|
||||||
g3.drawLine(X_1, Y_1, X_2, Y_2);
|
|
||||||
g3.drawLine(X_2, Y_1, X_1, Y_2);
|
|
||||||
}
|
|
||||||
|
|
||||||
g2.setColor(progress_FG);
|
|
||||||
g2.fillRect(0, PROGRESS_HEIGHT, progress, 1);
|
|
||||||
} finally {
|
|
||||||
g2.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
void onClick(final int x, final int y) {
|
|
||||||
// Check - we were over the 'X' (and thus no notify), or was it in the general area?
|
|
||||||
|
|
||||||
if (showCloseButton && x >= 280 && y <= 20) {
|
|
||||||
// reasonable position for detecting mouse over
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (onCloseAction != null) {
|
|
||||||
onCloseAction.handle(null);
|
|
||||||
}
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void setVisible(final boolean b) {
|
|
||||||
// was it already visible?
|
|
||||||
if (b == isVisible()) {
|
|
||||||
// prevent "double setting" visible state
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.setVisible(b);
|
|
||||||
|
|
||||||
if (b) {
|
|
||||||
toFront();
|
|
||||||
|
|
||||||
// set this jframe to use active rendering
|
|
||||||
SwingActiveRender.addActiveRender(this);
|
|
||||||
addPopupToMap();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
removePopupFromMap();
|
|
||||||
SwingActiveRender.removeActiveRender(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
void close() {
|
|
||||||
// this must happen in the Swing EDT. This is usually called by the active renderer
|
|
||||||
SwingUtil.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
// set it off screen (which is what the close method also does)
|
|
||||||
if (isVisible()) {
|
|
||||||
setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeAll();
|
|
||||||
removeWindowListener(windowListener);
|
|
||||||
removeMouseListener(mouseListener);
|
|
||||||
setIconImage(null);
|
|
||||||
dispose();
|
|
||||||
|
|
||||||
notification.onClose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// only called on the swing EDT thread
|
// only called on the swing EDT thread
|
||||||
void addPopupToMap() {
|
private static
|
||||||
|
void addPopupToMap(final LookAndFeel sourceLook) {
|
||||||
synchronized (popups) {
|
synchronized (popups) {
|
||||||
ArrayList<NotifyPopup> notifyPopups = popups.get(idAndPosition);
|
String id = sourceLook.idAndPosition;
|
||||||
if (notifyPopups == null) {
|
|
||||||
notifyPopups = new ArrayList<NotifyPopup>(4);
|
ArrayList<LookAndFeel> looks = popups.get(id);
|
||||||
popups.put(idAndPosition, notifyPopups);
|
if (looks == null) {
|
||||||
|
looks = new ArrayList<LookAndFeel>(4);
|
||||||
|
popups.put(id, looks);
|
||||||
}
|
}
|
||||||
final int popupIndex = notifyPopups.size();
|
final int popupIndex = looks.size();
|
||||||
this.popupIndex = popupIndex;
|
sourceLook.popupIndex = popupIndex;
|
||||||
|
|
||||||
// the popups are ALL the same size!
|
// the popups are ALL the same size!
|
||||||
// popups at TOP grow down, popups at BOTTOM grow up
|
// popups at TOP grow down, popups at BOTTOM grow up
|
||||||
|
|
||||||
int targetY;
|
int targetY;
|
||||||
if (isShowFromTop(position)) {
|
int anchorX = sourceLook.anchorX;
|
||||||
|
int anchorY = sourceLook.anchorY;
|
||||||
|
|
||||||
|
if (isShowFromTop(sourceLook)) {
|
||||||
targetY = anchorY + (popupIndex * (HEIGHT + 10));
|
targetY = anchorY + (popupIndex * (HEIGHT + 10));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
targetY = anchorY - (popupIndex * (HEIGHT + 10));
|
targetY = anchorY - (popupIndex * (HEIGHT + 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyPopups.add(this);
|
looks.add(sourceLook);
|
||||||
setLocation(anchorX, targetY);
|
sourceLook.setLocation(anchorX, targetY);
|
||||||
|
|
||||||
if (hideAfterDurationInSeconds > 0 && hideTween == null) {
|
if (sourceLook.hideAfterDurationInSeconds > 0 && sourceLook.hideTween == null) {
|
||||||
// begin a timeline to get rid of the popup (default is 5 seconds)
|
// begin a timeline to get rid of the popup (default is 5 seconds)
|
||||||
hideTween = Tween.to(this, NotifyPopupAccessor.PROGRESS, accessor, hideAfterDurationInSeconds)
|
Tween hideTween = Tween.to(sourceLook, NotifyAccessor.PROGRESS, accessor, sourceLook.hideAfterDurationInSeconds)
|
||||||
.target(WIDTH)
|
.target(WIDTH)
|
||||||
.ease(TweenEquations.Linear)
|
.ease(TweenEquations.Linear)
|
||||||
.addCallback(new TweenCallback() {
|
.addCallback(new TweenCallback() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onEvent(final int type, final BaseTween<?> source) {
|
void onEvent(final int type, final BaseTween<?> source) {
|
||||||
if (type == Events.END) {
|
if (type == Events.COMPLETE) {
|
||||||
close();
|
((INotify)sourceLook.parent).close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tweenManager.add(hideTween);
|
tweenManager.add(hideTween);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start if we have stopped the timer
|
// start if we have stopped the timer
|
||||||
if (!SwingActiveRender.containsActiveRenderFrameStart(frameStartHandler)) {
|
if (!SwingActiveRender.containsActiveRenderFrameStart(frameStartHandler)) {
|
||||||
|
@ -517,86 +519,92 @@ class NotifyPopup extends JFrame {
|
||||||
SwingActiveRender.addActiveRenderFrameStart(frameStartHandler);
|
SwingActiveRender.addActiveRenderFrameStart(frameStartHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
void setLocation(final int x, final int y) {
|
||||||
|
parent.setLocation(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// only called on the swing app or SwingActiveRender thread
|
// only called on the swing app or SwingActiveRender thread
|
||||||
private
|
private static
|
||||||
void removePopupFromMap() {
|
void removePopupFromMap(final LookAndFeel sourceLook) {
|
||||||
boolean showFromTop = isShowFromTop(position);
|
boolean showFromTop = isShowFromTop(sourceLook);
|
||||||
|
boolean popupsAreEmpty;
|
||||||
|
|
||||||
synchronized (popups) {
|
synchronized (popups) {
|
||||||
final ArrayList<NotifyPopup> notifyPopups = popups.get(idAndPosition);
|
popupsAreEmpty = popups.isEmpty();
|
||||||
|
final ArrayList<LookAndFeel> allLooks = popups.get(sourceLook.idAndPosition);
|
||||||
// there are two loops because it is necessary to kill + remove all tweens BEFORE adding new ones.
|
|
||||||
for (final NotifyPopup popup : notifyPopups) {
|
|
||||||
if (popup.tween != null) {
|
|
||||||
popup.tween.kill(); // kill does it's thing on the next tick of animation cycle
|
|
||||||
popup.tween = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (popup == this && popup.hideTween != null) {
|
|
||||||
popup.hideTween.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// there are two loops because it is necessary to cancel + remove all tweens BEFORE adding new ones.
|
||||||
boolean adjustPopupPosition = false;
|
boolean adjustPopupPosition = false;
|
||||||
for (Iterator<NotifyPopup> iterator = notifyPopups.iterator(); iterator.hasNext(); ) {
|
for (Iterator<LookAndFeel> iterator = allLooks.iterator(); iterator.hasNext(); ) {
|
||||||
final NotifyPopup popup = iterator.next();
|
final LookAndFeel look = iterator.next();
|
||||||
|
|
||||||
|
if (look.tween != null) {
|
||||||
|
look.tween.cancel(); // cancel does it's thing on the next tick of animation cycle
|
||||||
|
look.tween = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (look == sourceLook) {
|
||||||
|
if (look.hideTween != null) {
|
||||||
|
look.hideTween.cancel();
|
||||||
|
look.hideTween = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (popup == this) {
|
|
||||||
adjustPopupPosition = true;
|
adjustPopupPosition = true;
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
else if (adjustPopupPosition) {
|
|
||||||
int index = popup.popupIndex - 1;
|
|
||||||
popup.popupIndex = index;
|
|
||||||
|
|
||||||
|
if (adjustPopupPosition) {
|
||||||
|
look.popupIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final LookAndFeel look : allLooks) {
|
||||||
// the popups are ALL the same size!
|
// the popups are ALL the same size!
|
||||||
// popups at TOP grow down, popups at BOTTOM grow up
|
// popups at TOP grow down, popups at BOTTOM grow up
|
||||||
int changedY;
|
int changedY;
|
||||||
if (showFromTop) {
|
if (showFromTop) {
|
||||||
changedY = popup.anchorY + (index * (HEIGHT + 10));
|
changedY = look.anchorY + (look.popupIndex * (HEIGHT + 10));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
changedY = popup.anchorY - (index * (HEIGHT + 10));
|
changedY = look.anchorY - (look.popupIndex * (HEIGHT + 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
// now animate that popup to it's new location
|
// now animate that popup to it's new location
|
||||||
Tween tween = Tween.to(popup, NotifyPopupAccessor.Y_POS, accessor, MOVE_DURATION)
|
Tween tween = Tween.to(look, NotifyAccessor.Y_POS, accessor, MOVE_DURATION)
|
||||||
.target((float) changedY)
|
.target((float) changedY)
|
||||||
.ease(TweenEquations.Linear)
|
.ease(TweenEquations.Linear)
|
||||||
.addCallback(new TweenCallback() {
|
.addCallback(new TweenCallback() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void onEvent(final int type, final BaseTween<?> source) {
|
void onEvent(final int type, final BaseTween<?> source) {
|
||||||
// if (type == Events.END) {
|
if (type == Events.COMPLETE) {
|
||||||
// make sure to remove the tween once it's done, otherwise .kill can do weird things.
|
// make sure to remove the tween once it's done, otherwise .kill can do weird things.
|
||||||
popup.hideTween = null;
|
look.tween = null;
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
popup.tween = tween;
|
look.tween = tween;
|
||||||
tweenManager.add(tween);
|
tweenManager.add(tween);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there's nothing left, stop the timer.
|
// if there's nothing left, stop the timer.
|
||||||
if (popups.isEmpty()) {
|
if (popupsAreEmpty) {
|
||||||
SwingActiveRender.removeActiveRenderFrameStart(frameStartHandler);
|
SwingActiveRender.removeActiveRenderFrameStart(frameStartHandler);
|
||||||
}
|
}
|
||||||
// start if we have stopped the timer
|
// start if we have previously stopped the timer
|
||||||
else if (!SwingActiveRender.containsActiveRenderFrameStart(frameStartHandler)) {
|
else if (!SwingActiveRender.containsActiveRenderFrameStart(frameStartHandler)) {
|
||||||
tweenManager.resetUpdateTime();
|
tweenManager.resetUpdateTime();
|
||||||
SwingActiveRender.addActiveRenderFrameStart(frameStartHandler);
|
SwingActiveRender.addActiveRenderFrameStart(frameStartHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static
|
private static
|
||||||
boolean isShowFromTop(final Pos p) {
|
boolean isShowFromTop(final LookAndFeel look) {
|
||||||
switch (p) {
|
switch (look.position) {
|
||||||
case TOP_LEFT:
|
case TOP_LEFT:
|
||||||
case TOP_RIGHT:
|
case TOP_RIGHT:
|
||||||
case CENTER: // center grows down
|
case CENTER: // center grows down
|
||||||
|
@ -605,60 +613,4 @@ class NotifyPopup extends JFrame {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public
|
|
||||||
void setY(final int newY) {
|
|
||||||
setLocation(getX(), newY);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shakes the popup
|
|
||||||
*
|
|
||||||
* @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) {
|
|
||||||
int i1 = RANDOM.nextInt((amplitude << 2) + 1) - amplitude;
|
|
||||||
int i2 = RANDOM.nextInt((amplitude << 2) + 1) - amplitude;
|
|
||||||
|
|
||||||
i1 = i1 >> 2;
|
|
||||||
i2 = i2 >> 2;
|
|
||||||
|
|
||||||
// make sure it always moves by some amount
|
|
||||||
if (i1 < 0) {
|
|
||||||
i1 -= amplitude >> 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
i1 += amplitude >> 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i2 < 0) {
|
|
||||||
i2 -= amplitude >> 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
i2 += amplitude >> 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = durationInMillis / 50;
|
|
||||||
// make sure we always end the animation where we start
|
|
||||||
if ((count & 1) == 0) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tween tween = Tween.to(this, NotifyPopupAccessor.X_Y_POS, accessor, 0.05F)
|
|
||||||
.targetRelative(i1, i2)
|
|
||||||
.repeatAutoReverse(count, 0)
|
|
||||||
.ease(TweenEquations.Linear);
|
|
||||||
tweenManager.add(tween);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getProgress() {
|
|
||||||
return progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setProgress(final int progress) {
|
|
||||||
this.progress = progress;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
package dorkbox.notify;
|
package dorkbox.notify;
|
||||||
|
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
|
import java.awt.Window;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -54,6 +55,24 @@ import dorkbox.util.Version;
|
||||||
public final
|
public final
|
||||||
class Notify {
|
class Notify {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the title font used by a notification.
|
||||||
|
*/
|
||||||
|
@Property
|
||||||
|
public static String TITLE_TEXT_FONT = "Source Code Pro BOLD 16";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the main text font used by a notification.
|
||||||
|
*/
|
||||||
|
@Property
|
||||||
|
public static String MAIN_TEXT_FONT = "Source Code Pro BOLD 12";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How long we want it to take for the popups to relocate when one is closed
|
||||||
|
*/
|
||||||
|
@Property
|
||||||
|
public static float MOVE_DURATION = 1.0F;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Location of the dialog image resources. By default they must be in the 'resources' directory relative to the application
|
* Location of the dialog image resources. By default they must be in the 'resources' directory relative to the application
|
||||||
*/
|
*/
|
||||||
|
@ -140,11 +159,12 @@ class Notify {
|
||||||
private Image graphic;
|
private Image graphic;
|
||||||
|
|
||||||
ActionHandler<Notify> onCloseAction;
|
ActionHandler<Notify> onCloseAction;
|
||||||
private NotifyPopup notifyPopup;
|
private INotify notifyPopup;
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private int shakeDurationInMillis = 0;
|
private int shakeDurationInMillis = 0;
|
||||||
private int shakeAmplitude = 0;
|
private int shakeAmplitude = 0;
|
||||||
|
private Window window;
|
||||||
|
|
||||||
private
|
private
|
||||||
Notify() {
|
Notify() {
|
||||||
|
@ -268,7 +288,7 @@ class Notify {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the notification
|
* Shows the notification. If the Notification is assigned to a screen, but shown in a JFrame, the screen number will be ignored.
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
void show() {
|
void show() {
|
||||||
|
@ -281,12 +301,9 @@ class Notify {
|
||||||
final Notify notify = Notify.this;
|
final Notify notify = Notify.this;
|
||||||
final Image graphic = notify.graphic;
|
final Image graphic = notify.graphic;
|
||||||
|
|
||||||
if (graphic == null) {
|
|
||||||
notifyPopup = new NotifyPopup(notify, null, null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// we ONLY cache our own icons
|
// we ONLY cache our own icons
|
||||||
ImageIcon imageIcon;
|
ImageIcon imageIcon = null;
|
||||||
|
if (graphic != null) {
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
imageIcon = imageIconCache.get(name);
|
imageIcon = imageIconCache.get(name);
|
||||||
if (imageIcon == null) {
|
if (imageIcon == null) {
|
||||||
|
@ -301,8 +318,12 @@ class Notify {
|
||||||
else {
|
else {
|
||||||
imageIcon = new ImageIcon(graphic);
|
imageIcon = new ImageIcon(graphic);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
notifyPopup = new NotifyPopup(notify, graphic, imageIcon);
|
if (window == null) {
|
||||||
|
notifyPopup = new AsFrame(notify, graphic, imageIcon);
|
||||||
|
} else {
|
||||||
|
notifyPopup = new AsDialog(notify, graphic, imageIcon, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyPopup.setVisible(true);
|
notifyPopup.setVisible(true);
|
||||||
|
@ -312,6 +333,9 @@ class Notify {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// don't need to hang onto these.
|
||||||
|
graphic = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -369,9 +393,19 @@ class Notify {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches this notification to a specific JFrame/Window, instead of having a global notification
|
||||||
|
*/
|
||||||
|
public
|
||||||
|
Notify attach(final Window frame) {
|
||||||
|
this.window = frame;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// called when this notification is closed.
|
||||||
void onClose() {
|
void onClose() {
|
||||||
notifyPopup = null;
|
notifyPopup = null;
|
||||||
graphic = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,19 +17,19 @@ package dorkbox.notify;
|
||||||
|
|
||||||
import dorkbox.tweenengine.TweenAccessor;
|
import dorkbox.tweenengine.TweenAccessor;
|
||||||
|
|
||||||
class NotifyPopupAccessor implements TweenAccessor<NotifyPopup> {
|
class NotifyAccessor implements TweenAccessor<LookAndFeel> {
|
||||||
|
|
||||||
static final int Y_POS = 1;
|
static final int Y_POS = 1;
|
||||||
static final int X_Y_POS = 2;
|
static final int X_Y_POS = 2;
|
||||||
static final int PROGRESS = 3;
|
static final int PROGRESS = 3;
|
||||||
|
|
||||||
|
|
||||||
NotifyPopupAccessor() {
|
NotifyAccessor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
int getValues(final NotifyPopup target, final int tweenType, final float[] returnValues) {
|
int getValues(final LookAndFeel target, final int tweenType, final float[] returnValues) {
|
||||||
switch (tweenType) {
|
switch (tweenType) {
|
||||||
case Y_POS:
|
case Y_POS:
|
||||||
returnValues[0] = (float) target.getY();
|
returnValues[0] = (float) target.getY();
|
||||||
|
@ -48,7 +48,7 @@ class NotifyPopupAccessor implements TweenAccessor<NotifyPopup> {
|
||||||
@SuppressWarnings({"NumericCastThatLosesPrecision", "UnnecessaryReturnStatement"})
|
@SuppressWarnings({"NumericCastThatLosesPrecision", "UnnecessaryReturnStatement"})
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setValues(final NotifyPopup target, final int tweenType, final float[] newValues) {
|
void setValues(final LookAndFeel target, final int tweenType, final float[] newValues) {
|
||||||
switch (tweenType) {
|
switch (tweenType) {
|
||||||
case Y_POS:
|
case Y_POS:
|
||||||
target.setY((int) newValues[0]);
|
target.setY((int) newValues[0]);
|
|
@ -15,22 +15,23 @@
|
||||||
*/
|
*/
|
||||||
package dorkbox.notify;
|
package dorkbox.notify;
|
||||||
|
|
||||||
import java.awt.event.WindowAdapter;
|
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
|
|
||||||
class NotifyPopupWindowAdapter extends WindowAdapter {
|
class WindowAdapter extends java.awt.event.WindowAdapter {
|
||||||
|
@Override
|
||||||
public
|
public
|
||||||
void windowClosing(WindowEvent e) {
|
void windowClosing(WindowEvent e) {
|
||||||
if (e.getNewState() != WindowEvent.WINDOW_CLOSED) {
|
if (e.getNewState() != WindowEvent.WINDOW_CLOSED) {
|
||||||
NotifyPopup source = (NotifyPopup) e.getSource();
|
AsFrame source = (AsFrame) e.getSource();
|
||||||
source.close();
|
source.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public
|
public
|
||||||
void windowLostFocus(WindowEvent e) {
|
void windowLostFocus(WindowEvent e) {
|
||||||
if (e.getNewState() != WindowEvent.WINDOW_CLOSED) {
|
if (e.getNewState() != WindowEvent.WINDOW_CLOSED) {
|
||||||
NotifyPopup source = (NotifyPopup) e.getSource();
|
AsFrame source = (AsFrame) e.getSource();
|
||||||
// these don't work
|
// these don't work
|
||||||
//toFront();
|
//toFront();
|
||||||
//requestFocus();
|
//requestFocus();
|
|
@ -13,9 +13,17 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
import dorkbox.notify.Notify;
|
import dorkbox.notify.Notify;
|
||||||
import dorkbox.notify.Pos;
|
import dorkbox.notify.Pos;
|
||||||
import dorkbox.util.ActionHandler;
|
import dorkbox.util.ActionHandler;
|
||||||
|
import dorkbox.util.ScreenUtil;
|
||||||
|
|
||||||
public
|
public
|
||||||
class NotifyTest {
|
class NotifyTest {
|
||||||
|
@ -24,28 +32,117 @@ class NotifyTest {
|
||||||
void main(String[] args) {
|
void main(String[] args) {
|
||||||
Notify notify;
|
Notify notify;
|
||||||
|
|
||||||
int count = 3;
|
|
||||||
|
JFrame frame = new JFrame("Test");
|
||||||
|
|
||||||
|
JPanel panel = new JPanel();
|
||||||
|
panel.setLayout(new FlowLayout());
|
||||||
|
|
||||||
|
JLabel label = new JLabel("This is a label!");
|
||||||
|
|
||||||
|
JButton button = new JButton();
|
||||||
|
button.setText("Press me");
|
||||||
|
|
||||||
|
panel.add(label);
|
||||||
|
panel.add(button);
|
||||||
|
|
||||||
|
frame.add(panel);
|
||||||
|
frame.setSize(600, 600);
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
ScreenUtil.showOnSameScreenAsMouse_Center(frame);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int count = 2;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
|
final int finalI = i;
|
||||||
notify = Notify.create()
|
notify = Notify.create()
|
||||||
.title("Notify title " + i)
|
.title("Notify title " + i)
|
||||||
.text("This is a notification " + i + " popup message This is a notification popup message This is a " +
|
.text("This is a notification " + i + " popup message This is a notification popup message This is a " +
|
||||||
"notification popup message")
|
"notification popup message")
|
||||||
.hideAfter(50000)
|
.hideAfter(13000)
|
||||||
.position(Pos.TOP_RIGHT)
|
.position(Pos.CENTER)
|
||||||
// .setScreen(0)
|
// .setScreen(0)
|
||||||
.darkStyle()
|
.darkStyle()
|
||||||
.shake(1300, 4)
|
// .shake(1300, 4)
|
||||||
// .shake(1300, 10)
|
// .shake(1300, 10)
|
||||||
// .hideCloseButton()
|
.attach(frame)
|
||||||
|
.hideCloseButton()
|
||||||
.onAction(new ActionHandler<Notify>() {
|
.onAction(new ActionHandler<Notify>() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void handle(final Notify arg0) {
|
void handle(final Notify arg0) {
|
||||||
System.out.println("Notification clicked on!");
|
System.err.println("Notification " + finalI + " clicked on!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
notify.showWarning();
|
notify.showWarning();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
final int finalI = i;
|
||||||
|
notify = Notify.create()
|
||||||
|
.title("Notify title " + i)
|
||||||
|
.text("This is a notification " + i + " popup message This is a notification popup message This is a " +
|
||||||
|
"notification popup message")
|
||||||
|
.hideAfter(13000)
|
||||||
|
.position(Pos.TOP_RIGHT)
|
||||||
|
// .setScreen(0)
|
||||||
|
.darkStyle()
|
||||||
|
// .shake(1300, 4)
|
||||||
|
// .shake(1300, 10)
|
||||||
|
.hideCloseButton()
|
||||||
|
.onAction(new ActionHandler<Notify>() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void handle(final Notify arg0) {
|
||||||
|
System.err.println("Notification " + finalI + " clicked on!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
notify.showConfirm();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
final int finalI = i;
|
||||||
|
notify = Notify.create()
|
||||||
|
.title("Notify title " + i)
|
||||||
|
.text("This is a notification " + i + " popup message This is a notification popup message This is a " +
|
||||||
|
"notification popup message")
|
||||||
|
.hideAfter(13000)
|
||||||
|
.position(Pos.BOTTOM_LEFT)
|
||||||
|
// .setScreen(0)
|
||||||
|
.darkStyle()
|
||||||
|
// .shake(1300, 4)
|
||||||
|
// .shake(1300, 10)
|
||||||
|
.hideCloseButton()
|
||||||
|
.onAction(new ActionHandler<Notify>() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void handle(final Notify arg0) {
|
||||||
|
System.err.println("Notification " + finalI + " clicked on!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
notify.show();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user