forked from dorkbox/SystemTray
Converted SystemTray to use a singleton pattern for ease of use. Icon *must* be set to see it (obviously). Updated Readme.md example.
This commit is contained in:
parent
801baad635
commit
8b109f6d1c
22
README.md
22
README.md
@ -44,24 +44,22 @@ GnomeShellExtension.SHELL_RESTART_COMMAND (type String, default value 'gnome-s
|
|||||||
SystemTray.TRAY_SIZE (type int, default value '24')
|
SystemTray.TRAY_SIZE (type int, default value '24')
|
||||||
- Size of the tray, so that the icon can properly scale based on OS. (if it's not exact). This only applies for Swing tray icons.
|
- Size of the tray, so that the icon can properly scale based on OS. (if it's not exact). This only applies for Swing tray icons.
|
||||||
- NOTE: Must be set after any other customization options, as a static call to SystemTray will cause initialization of the library.
|
- NOTE: Must be set after any other customization options, as a static call to SystemTray will cause initialization of the library.
|
||||||
|
|
||||||
|
|
||||||
SystemTray.ICON_PATH (type String, default value '')
|
|
||||||
- Location of the icon (to make it easier when specifying icons)
|
|
||||||
- NOTE: Must be set after any other customization options, as a static call to SystemTray will cause initialization of the library.
|
|
||||||
of the library.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The test application is [on GitHub](https://github.com/dorkbox/SystemTray/blob/master/test/dorkbox/TestTray.java), and a *simple* example is as follows:
|
The test application is [on GitHub](https://github.com/dorkbox/SystemTray/blob/master/test/dorkbox/TestTray.java), and a *simple* example is as follows:
|
||||||
```
|
```
|
||||||
// if using provided JNA jars. Not necessary if
|
this.systemTray = SystemTray.getSystemTray();
|
||||||
//using JNA from https://github.com/twall/jna
|
if (systemTray == null) {
|
||||||
System.load("Path to OS specific JNA jar");
|
throw new RuntimeException("Unable to load SystemTray!");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
this.systemTray = SystemTray.create("grey_icon.png");
|
this.systemTray.setIcon("grey_icon.png");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
this.systemTray.setStatus("Not Running");
|
this.systemTray.setStatus("Not Running");
|
||||||
|
|
||||||
@ -135,7 +133,7 @@ This project is **kept in sync** with the utilities library, so "jar hell" is no
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.dorkbox</groupId>
|
<groupId>com.dorkbox</groupId>
|
||||||
<artifactId>SystemTray</artifactId>
|
<artifactId>SystemTray</artifactId>
|
||||||
<version>1.15</version>
|
<version>2.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ package dorkbox.systemTray;
|
|||||||
|
|
||||||
import dorkbox.systemTray.linux.AppIndicatorTray;
|
import dorkbox.systemTray.linux.AppIndicatorTray;
|
||||||
import dorkbox.systemTray.linux.GnomeShellExtension;
|
import dorkbox.systemTray.linux.GnomeShellExtension;
|
||||||
|
import dorkbox.systemTray.linux.GtkSystemTray;
|
||||||
import dorkbox.systemTray.swing.SwingSystemTray;
|
import dorkbox.systemTray.swing.SwingSystemTray;
|
||||||
import dorkbox.util.OS;
|
import dorkbox.util.OS;
|
||||||
import dorkbox.util.Property;
|
import dorkbox.util.Property;
|
||||||
@ -24,7 +25,6 @@ import dorkbox.util.jna.linux.AppIndicator;
|
|||||||
import dorkbox.util.jna.linux.AppIndicatorQuery;
|
import dorkbox.util.jna.linux.AppIndicatorQuery;
|
||||||
import dorkbox.util.jna.linux.GtkSupport;
|
import dorkbox.util.jna.linux.GtkSupport;
|
||||||
import dorkbox.util.process.ShellProcessBuilder;
|
import dorkbox.util.process.ShellProcessBuilder;
|
||||||
import dorkbox.systemTray.linux.GtkSystemTray;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ import java.util.Iterator;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for system tray implementations.
|
* Factory and base-class for system tray implementations.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public abstract
|
public abstract
|
||||||
@ -53,11 +53,13 @@ class SystemTray {
|
|||||||
/** Size of the tray, so that the icon can properly scale based on OS. (if it's not exact) */
|
/** Size of the tray, so that the icon can properly scale based on OS. (if it's not exact) */
|
||||||
public static int TRAY_SIZE = 22;
|
public static int TRAY_SIZE = 22;
|
||||||
|
|
||||||
private static Class<? extends SystemTray> trayType;
|
private static final SystemTray systemTray;
|
||||||
|
|
||||||
static boolean isKDE = false;
|
static boolean isKDE = false;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
Class<? extends SystemTray> trayType = null;
|
||||||
|
|
||||||
// Note: AppIndicators DO NOT support tooltips. We could try to create one, by creating a GTK widget and attaching it on
|
// Note: AppIndicators DO NOT support tooltips. We could try to create one, by creating a GTK widget and attaching it on
|
||||||
// mouseover or something, but I don't know how to do that. It seems that tooltips for app-indicators are a custom job, as
|
// mouseover or something, but I don't know how to do that. It seems that tooltips for app-indicators are a custom job, as
|
||||||
// all examined ones sometimes have it (and it's more than just text), or they don't have it at all.
|
// all examined ones sometimes have it (and it's more than just text), or they don't have it at all.
|
||||||
@ -215,9 +217,6 @@ class SystemTray {
|
|||||||
// fallback...
|
// fallback...
|
||||||
if (trayType == null) {
|
if (trayType == null) {
|
||||||
trayType = GtkSystemTray.class;
|
trayType = GtkSystemTray.class;
|
||||||
}
|
|
||||||
|
|
||||||
if (trayType == null) {
|
|
||||||
logger.error("Unable to load the system tray native library. Please write an issue and include your OS type and " +
|
logger.error("Unable to load the system tray native library. Please write an issue and include your OS type and " +
|
||||||
"configuration");
|
"configuration");
|
||||||
}
|
}
|
||||||
@ -226,20 +225,30 @@ class SystemTray {
|
|||||||
|
|
||||||
// this is windows OR mac
|
// this is windows OR mac
|
||||||
if (trayType == null && java.awt.SystemTray.isSupported()) {
|
if (trayType == null && java.awt.SystemTray.isSupported()) {
|
||||||
trayType = SwingSystemTray.class;
|
try {
|
||||||
|
java.awt.SystemTray.getSystemTray();
|
||||||
|
trayType = SwingSystemTray.class;
|
||||||
|
} catch (Throwable ignored) {
|
||||||
|
logger.error("Maybe you should grant the AWTPermission `accessSystemTray` in the SecurityManager.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trayType == null) {
|
if (trayType == null) {
|
||||||
// unsupported tray
|
// unsupported tray
|
||||||
logger.error("Unsupported tray type!");
|
logger.error("Unsupported tray type!");
|
||||||
|
systemTray = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
SystemTray systemTray_ = null;
|
||||||
try {
|
try {
|
||||||
ImageUtil.init();
|
ImageUtil.init();
|
||||||
|
systemTray_ = (SystemTray) trayType.getConstructors()[0].newInstance();
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
logger.error("Unsupported hashing algorithm!");
|
logger.error("Unsupported hashing algorithm!");
|
||||||
trayType = null;
|
} catch (Exception e) {
|
||||||
|
logger.error("Unable to create tray type: '" + trayType.getSimpleName() + "'");
|
||||||
}
|
}
|
||||||
|
systemTray = systemTray_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,113 +257,21 @@ class SystemTray {
|
|||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
String getVersion() {
|
String getVersion() {
|
||||||
return "1.15";
|
return "2.1";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will directly use the
|
* This always returns the same instance per JVM (it's a singleton), and on some platforms the system tray may not be
|
||||||
* contents of the specified file.
|
* supported, in which case this will return NULL.
|
||||||
*
|
*
|
||||||
* @param iconPath the full path for an icon to use
|
* <p>If this is using the Swing SystemTray and a SecurityManager is installed, the AWTPermission {@code accessSystemTray} must
|
||||||
*
|
* be granted in order to get the {@code SystemTray} instance. Otherwise this will return null.
|
||||||
* @return a new SystemTray instance with the specified path for the icon
|
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
SystemTray create(String iconPath) {
|
SystemTray getSystemTray() {
|
||||||
if (trayType != null) {
|
return systemTray;
|
||||||
try {
|
|
||||||
iconPath = ImageUtil.iconPath(iconPath);
|
|
||||||
Object o = trayType.getConstructors()[0].newInstance(iconPath);
|
|
||||||
return (SystemTray) o;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsupported
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
|
||||||
* the URL to a temporary location on disk, based on the path specified by the URL.
|
|
||||||
*
|
|
||||||
* @param iconUrl the URL for the icon to use
|
|
||||||
*
|
|
||||||
* @return a new SystemTray instance with the specified URL for the icon
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
SystemTray create(final URL iconUrl) {
|
|
||||||
if (trayType != null) {
|
|
||||||
try {
|
|
||||||
String iconPath = ImageUtil.iconPath(iconUrl);
|
|
||||||
Object o = trayType.getConstructors()[0].newInstance(iconPath);
|
|
||||||
return (SystemTray) o;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsupported
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
|
||||||
* the iconStream to a temporary location on disk, based on the `cacheName` specified.
|
|
||||||
*
|
|
||||||
* @param cacheName the name to use for the cache lookup for the iconStream. This can be anything you want, but should be
|
|
||||||
* consistently unique
|
|
||||||
* @param iconStream the InputStream to load the icon from
|
|
||||||
*
|
|
||||||
* @return a new SystemTray instance with the specified InputStream for the icon
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
SystemTray create(final String cacheName, final InputStream iconStream) {
|
|
||||||
if (trayType != null) {
|
|
||||||
try {
|
|
||||||
String iconPath = ImageUtil.iconPath(cacheName, iconStream);
|
|
||||||
Object o = trayType.getConstructors()[0].newInstance(iconPath);
|
|
||||||
return (SystemTray) o;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsupported
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
|
||||||
* the iconStream to a temporary location on disk.
|
|
||||||
*
|
|
||||||
* This method **DOES NOT CACHE** the result, so multiple lookups for the same inputStream result in new files every time. This is
|
|
||||||
* also NOT RECOMMENDED, but is provided for simplicity.
|
|
||||||
*
|
|
||||||
* @param iconStream the InputStream to load the icon from
|
|
||||||
*
|
|
||||||
* @return a new SystemTray instance with the specified InputStream for the icon
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static
|
|
||||||
SystemTray create(final InputStream iconStream) {
|
|
||||||
if (trayType != null) {
|
|
||||||
try {
|
|
||||||
String iconPath = ImageUtil.iconPathNoCache(iconStream);
|
|
||||||
Object o = trayType.getConstructors()[0].newInstance(iconPath);
|
|
||||||
return (SystemTray) o;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsupported
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected final java.util.List<MenuEntry> menuEntries = new ArrayList<MenuEntry>();
|
protected final java.util.List<MenuEntry> menuEntries = new ArrayList<MenuEntry>();
|
||||||
|
|
||||||
protected
|
protected
|
||||||
@ -391,6 +308,9 @@ class SystemTray {
|
|||||||
/**
|
/**
|
||||||
* Changes the tray icon used.
|
* Changes the tray icon used.
|
||||||
*
|
*
|
||||||
|
* Because the cross-platform, underlying system uses a file path to load icons for the system tray,
|
||||||
|
* this will directly use the contents of the specified file.
|
||||||
|
*
|
||||||
* @param imagePath the path of the icon to use
|
* @param imagePath the path of the icon to use
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
@ -402,6 +322,9 @@ class SystemTray {
|
|||||||
/**
|
/**
|
||||||
* Changes the tray icon used.
|
* Changes the tray icon used.
|
||||||
*
|
*
|
||||||
|
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||||
|
* the URL to a temporary location on disk, based on the path specified by the URL.
|
||||||
|
*
|
||||||
* @param imageUrl the URL of the icon to use
|
* @param imageUrl the URL of the icon to use
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
@ -413,6 +336,9 @@ class SystemTray {
|
|||||||
/**
|
/**
|
||||||
* Changes the tray icon used.
|
* Changes the tray icon used.
|
||||||
*
|
*
|
||||||
|
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||||
|
* the imageStream to a temporary location on disk, based on the `cacheName` specified.
|
||||||
|
*
|
||||||
* @param cacheName the name to use for lookup in the cache for the iconStream
|
* @param cacheName the name to use for lookup in the cache for the iconStream
|
||||||
* @param imageStream the InputStream of the icon to use
|
* @param imageStream the InputStream of the icon to use
|
||||||
*/
|
*/
|
||||||
@ -425,6 +351,9 @@ class SystemTray {
|
|||||||
/**
|
/**
|
||||||
* Changes the tray icon used.
|
* Changes the tray icon used.
|
||||||
*
|
*
|
||||||
|
* Because the cross-platform, underlying system uses a file path to load icons for the system tray, this will copy the contents of
|
||||||
|
* the imageStream to a temporary location on disk.
|
||||||
|
*
|
||||||
* This method **DOES NOT CACHE** the result, so multiple lookups for the same inputStream result in new files every time. This is
|
* This method **DOES NOT CACHE** the result, so multiple lookups for the same inputStream result in new files every time. This is
|
||||||
* also NOT RECOMMENDED, but is provided for simplicity.
|
* also NOT RECOMMENDED, but is provided for simplicity.
|
||||||
*
|
*
|
||||||
|
@ -33,15 +33,14 @@ class AppIndicatorTray extends GtkTypeSystemTray {
|
|||||||
private static final AppIndicator appindicator = AppIndicator.INSTANCE;
|
private static final AppIndicator appindicator = AppIndicator.INSTANCE;
|
||||||
|
|
||||||
private AppIndicator.AppIndicatorInstanceStruct appIndicator;
|
private AppIndicator.AppIndicatorInstanceStruct appIndicator;
|
||||||
|
private volatile boolean isActive = false;
|
||||||
|
|
||||||
public
|
public
|
||||||
AppIndicatorTray(String iconPath) {
|
AppIndicatorTray() {
|
||||||
gtk.gdk_threads_enter();
|
gtk.gdk_threads_enter();
|
||||||
|
|
||||||
this.appIndicator = appindicator.app_indicator_new(System.nanoTime() + "DBST", iconPath,
|
this.appIndicator = appindicator.app_indicator_new(System.nanoTime() + "DBST", "",
|
||||||
AppIndicator.CATEGORY_APPLICATION_STATUS);
|
AppIndicator.CATEGORY_APPLICATION_STATUS);
|
||||||
appindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_ACTIVE);
|
|
||||||
|
|
||||||
gtk.gdk_threads_leave();
|
gtk.gdk_threads_leave();
|
||||||
|
|
||||||
GtkSupport.startGui();
|
GtkSupport.startGui();
|
||||||
@ -69,6 +68,13 @@ class AppIndicatorTray extends GtkTypeSystemTray {
|
|||||||
void setIcon_(final String iconPath) {
|
void setIcon_(final String iconPath) {
|
||||||
gtk.gdk_threads_enter();
|
gtk.gdk_threads_enter();
|
||||||
appindicator.app_indicator_set_icon(this.appIndicator, iconPath);
|
appindicator.app_indicator_set_icon(this.appIndicator, iconPath);
|
||||||
|
|
||||||
|
if (!isActive) {
|
||||||
|
isActive = true;
|
||||||
|
|
||||||
|
appindicator.app_indicator_set_status(this.appIndicator, AppIndicator.STATUS_ACTIVE);
|
||||||
|
}
|
||||||
|
|
||||||
gtk.gdk_threads_leave();
|
gtk.gdk_threads_leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,11 +36,11 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
|||||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||||
private NativeLong button_press_event;
|
private NativeLong button_press_event;
|
||||||
|
|
||||||
|
private volatile boolean isActive = false;
|
||||||
private volatile Pointer menu;
|
private volatile Pointer menu;
|
||||||
|
|
||||||
public
|
public
|
||||||
GtkSystemTray(String iconPath) {
|
GtkSystemTray() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
gtk.gdk_threads_enter();
|
gtk.gdk_threads_enter();
|
||||||
@ -51,8 +51,6 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
|||||||
|
|
||||||
this.trayIcon = trayIcon;
|
this.trayIcon = trayIcon;
|
||||||
|
|
||||||
gtk.gtk_status_icon_set_from_file(trayIcon, iconPath);
|
|
||||||
|
|
||||||
this.gtkCallback = new Gobject.GEventCallback() {
|
this.gtkCallback = new Gobject.GEventCallback() {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
@ -65,8 +63,6 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
|||||||
};
|
};
|
||||||
button_press_event = gobject.g_signal_connect_data(trayIcon, "button_press_event", gtkCallback, null, null, 0);
|
button_press_event = gobject.g_signal_connect_data(trayIcon, "button_press_event", gtkCallback, null, null, 0);
|
||||||
|
|
||||||
gtk.gtk_status_icon_set_visible(trayIcon, true);
|
|
||||||
|
|
||||||
gtk.gdk_threads_leave();
|
gtk.gdk_threads_leave();
|
||||||
|
|
||||||
GtkSupport.startGui();
|
GtkSupport.startGui();
|
||||||
@ -101,7 +97,13 @@ class GtkSystemTray extends GtkTypeSystemTray {
|
|||||||
protected synchronized
|
protected synchronized
|
||||||
void setIcon_(final String iconPath) {
|
void setIcon_(final String iconPath) {
|
||||||
gtk.gdk_threads_enter();
|
gtk.gdk_threads_enter();
|
||||||
|
|
||||||
gtk.gtk_status_icon_set_from_file(trayIcon, iconPath);
|
gtk.gtk_status_icon_set_from_file(trayIcon, iconPath);
|
||||||
|
|
||||||
|
if (!isActive) {
|
||||||
|
isActive = true;
|
||||||
|
gtk.gtk_status_icon_set_visible(trayIcon, true);
|
||||||
|
}
|
||||||
gtk.gdk_threads_leave();
|
gtk.gdk_threads_leave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
package dorkbox.systemTray.swing;
|
package dorkbox.systemTray.swing;
|
||||||
|
|
||||||
import dorkbox.systemTray.ImageUtil;
|
import dorkbox.systemTray.ImageUtil;
|
||||||
import dorkbox.util.ScreenUtil;
|
|
||||||
import dorkbox.util.SwingUtil;
|
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
import dorkbox.systemTray.SystemTrayMenuPopup;
|
import dorkbox.systemTray.SystemTrayMenuPopup;
|
||||||
|
import dorkbox.util.ScreenUtil;
|
||||||
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JMenuItem;
|
import javax.swing.JMenuItem;
|
||||||
@ -49,11 +49,13 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
volatile SystemTray tray;
|
volatile SystemTray tray;
|
||||||
volatile TrayIcon trayIcon;
|
volatile TrayIcon trayIcon;
|
||||||
|
|
||||||
|
volatile boolean isActive = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new system tray handler class.
|
* Creates a new system tray handler class.
|
||||||
*/
|
*/
|
||||||
public
|
public
|
||||||
SwingSystemTray(final String iconPath) {
|
SwingSystemTray() {
|
||||||
super();
|
super();
|
||||||
SwingUtil.invokeAndWait(new Runnable() {
|
SwingUtil.invokeAndWait(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -63,63 +65,6 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
if (SwingSystemTray.this.tray == null) {
|
if (SwingSystemTray.this.tray == null) {
|
||||||
logger.error("The system tray is not available");
|
logger.error("The system tray is not available");
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
SwingSystemTray.this.menu = new SystemTrayMenuPopup();
|
|
||||||
|
|
||||||
Image trayImage = new ImageIcon(iconPath).getImage()
|
|
||||||
.getScaledInstance(TRAY_SIZE, TRAY_SIZE, Image.SCALE_SMOOTH);
|
|
||||||
trayImage.flush();
|
|
||||||
final TrayIcon trayIcon = new TrayIcon(trayImage);
|
|
||||||
SwingSystemTray.this.trayIcon = trayIcon;
|
|
||||||
|
|
||||||
// appindicators don't support this, so we cater to the lowest common denominator
|
|
||||||
// trayIcon.setToolTip(SwingSystemTray.this.appName);
|
|
||||||
|
|
||||||
trayIcon.addMouseListener(new MouseAdapter() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void mousePressed(MouseEvent e) {
|
|
||||||
final SystemTrayMenuPopup menu = SwingSystemTray.this.menu;
|
|
||||||
Dimension size = menu.getPreferredSize();
|
|
||||||
|
|
||||||
Point point = e.getPoint();
|
|
||||||
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
|
||||||
|
|
||||||
int x = point.x;
|
|
||||||
int y = point.y;
|
|
||||||
|
|
||||||
if (y < bounds.y) {
|
|
||||||
y = bounds.y;
|
|
||||||
}
|
|
||||||
else if (y + size.height > bounds.y + bounds.height) {
|
|
||||||
// our menu cannot have the top-edge snap to the mouse
|
|
||||||
// so we make the bottom-edge snap to the mouse
|
|
||||||
y -= size.height; // snap to edge of mouse
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x < bounds.x) {
|
|
||||||
x = bounds.x;
|
|
||||||
}
|
|
||||||
else if (x + size.width > bounds.x + bounds.width) {
|
|
||||||
// our menu cannot have the left-edge snap to the mouse
|
|
||||||
// so we make the right-edge snap to the mouse
|
|
||||||
x -= size.width; // snap to edge of mouse
|
|
||||||
}
|
|
||||||
|
|
||||||
// weird voodoo to get this to popup with the correct parent
|
|
||||||
menu.setInvoker(menu);
|
|
||||||
menu.setLocation(x, y);
|
|
||||||
menu.setVisible(true);
|
|
||||||
menu.requestFocus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
SwingSystemTray.this.tray.add(trayIcon);
|
|
||||||
} catch (AWTException e) {
|
|
||||||
logger.error("TrayIcon could not be added.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -184,10 +129,70 @@ class SwingSystemTray extends dorkbox.systemTray.SystemTray {
|
|||||||
void run() {
|
void run() {
|
||||||
SwingSystemTray tray = SwingSystemTray.this;
|
SwingSystemTray tray = SwingSystemTray.this;
|
||||||
synchronized (tray) {
|
synchronized (tray) {
|
||||||
Image trayImage = new ImageIcon(iconPath).getImage()
|
if (!isActive) {
|
||||||
.getScaledInstance(TRAY_SIZE, TRAY_SIZE, Image.SCALE_SMOOTH);
|
isActive = true;
|
||||||
trayImage.flush();
|
|
||||||
tray.trayIcon.setImage(trayImage);
|
SwingSystemTray.this.menu = new SystemTrayMenuPopup();
|
||||||
|
|
||||||
|
Image trayImage = new ImageIcon(iconPath).getImage()
|
||||||
|
.getScaledInstance(TRAY_SIZE, TRAY_SIZE, Image.SCALE_SMOOTH);
|
||||||
|
trayImage.flush();
|
||||||
|
final TrayIcon trayIcon = new TrayIcon(trayImage);
|
||||||
|
SwingSystemTray.this.trayIcon = trayIcon;
|
||||||
|
|
||||||
|
// appindicators don't support this, so we cater to the lowest common denominator
|
||||||
|
// trayIcon.setToolTip(SwingSystemTray.this.appName);
|
||||||
|
|
||||||
|
trayIcon.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void mousePressed(MouseEvent e) {
|
||||||
|
final SystemTrayMenuPopup menu = SwingSystemTray.this.menu;
|
||||||
|
Dimension size = menu.getPreferredSize();
|
||||||
|
|
||||||
|
Point point = e.getPoint();
|
||||||
|
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
|
||||||
|
|
||||||
|
int x = point.x;
|
||||||
|
int y = point.y;
|
||||||
|
|
||||||
|
if (y < bounds.y) {
|
||||||
|
y = bounds.y;
|
||||||
|
}
|
||||||
|
else if (y + size.height > bounds.y + bounds.height) {
|
||||||
|
// our menu cannot have the top-edge snap to the mouse
|
||||||
|
// so we make the bottom-edge snap to the mouse
|
||||||
|
y -= size.height; // snap to edge of mouse
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x < bounds.x) {
|
||||||
|
x = bounds.x;
|
||||||
|
}
|
||||||
|
else if (x + size.width > bounds.x + bounds.width) {
|
||||||
|
// our menu cannot have the left-edge snap to the mouse
|
||||||
|
// so we make the right-edge snap to the mouse
|
||||||
|
x -= size.width; // snap to edge of mouse
|
||||||
|
}
|
||||||
|
|
||||||
|
// weird voodoo to get this to popup with the correct parent
|
||||||
|
menu.setInvoker(menu);
|
||||||
|
menu.setLocation(x, y);
|
||||||
|
menu.setVisible(true);
|
||||||
|
menu.requestFocus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
SwingSystemTray.this.tray.add(trayIcon);
|
||||||
|
} catch (AWTException e) {
|
||||||
|
logger.error("TrayIcon could not be added.", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Image trayImage = new ImageIcon(iconPath).getImage()
|
||||||
|
.getScaledInstance(TRAY_SIZE, TRAY_SIZE, Image.SCALE_SMOOTH);
|
||||||
|
trayImage.flush();
|
||||||
|
tray.trayIcon.setImage(trayImage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -20,6 +20,7 @@ import dorkbox.systemTray.MenuEntry;
|
|||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
@ -36,11 +37,11 @@ class TestTray {
|
|||||||
|
|
||||||
public static
|
public static
|
||||||
void main(String[] args) {
|
void main(String[] args) {
|
||||||
// ONLY if manually loading JNA jars (which is how i do it).
|
// ONLY if manually loading JNA jars.
|
||||||
//
|
//
|
||||||
// Not necessary if using the official JNA downloaded from https://github.com/twall/jna AND THAT JAR is on the classpath
|
// Not necessary if using the official JNA downloaded from https://github.com/twall/jna AND THAT JAR is on the classpath
|
||||||
//
|
//
|
||||||
// System.load(new File("../../resources/Dependencies/jna/linux_64/libjna.so").getAbsolutePath()); //64bit linux library
|
System.load(new File("../../resources/Dependencies/jna/linux_64/libjna.so").getAbsolutePath()); //64bit linux library
|
||||||
|
|
||||||
new TestTray();
|
new TestTray();
|
||||||
}
|
}
|
||||||
@ -51,11 +52,17 @@ class TestTray {
|
|||||||
|
|
||||||
public
|
public
|
||||||
TestTray() {
|
TestTray() {
|
||||||
this.systemTray = SystemTray.create(LT_GRAY_MAIL);
|
this.systemTray = SystemTray.getSystemTray();
|
||||||
if (systemTray == null) {
|
if (systemTray == null) {
|
||||||
throw new RuntimeException("Unable to load SystemTray!");
|
throw new RuntimeException("Unable to load SystemTray!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.systemTray.setIcon(LT_GRAY_MAIL);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
systemTray.setStatus("No Mail");
|
systemTray.setStatus("No Mail");
|
||||||
|
|
||||||
callbackGreen = new SystemTrayMenuAction() {
|
callbackGreen = new SystemTrayMenuAction() {
|
||||||
|
Loading…
Reference in New Issue
Block a user