WIP swing submenus
This commit is contained in:
parent
7509d7c960
commit
b354daab68
|
@ -17,17 +17,30 @@ package dorkbox.systemTray.swing;
|
||||||
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
|
||||||
import dorkbox.systemTray.Menu;
|
import dorkbox.systemTray.Menu;
|
||||||
import dorkbox.systemTray.MenuEntry;
|
import dorkbox.systemTray.MenuEntry;
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.SystemTrayMenuAction;
|
import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
import dorkbox.util.SwingUtil;
|
import dorkbox.util.SwingUtil;
|
||||||
|
|
||||||
|
// this is a weird composite class, because it must be a Menu, but ALSO a MenuEntry -- so it has both
|
||||||
public
|
public
|
||||||
class SwingMenu extends Menu {
|
class SwingMenu extends Menu implements MenuEntry {
|
||||||
|
|
||||||
volatile SwingSystemTrayMenuPopup _native;
|
volatile JComponent _native;
|
||||||
|
|
||||||
|
// this have to be volatile, because they can be changed from any thread
|
||||||
|
private volatile String text;
|
||||||
|
private volatile boolean hasLegitIcon = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param systemTray
|
* @param systemTray
|
||||||
|
@ -43,7 +56,11 @@ class SwingMenu extends Menu {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
_native = new SwingSystemTrayMenuPopup();
|
_native = new JMenu();
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
((SwingMenu) parent)._native.add(_native);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -92,9 +109,16 @@ class SwingMenu extends Menu {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// must always be called on the EDT
|
// must always be called on the EDT
|
||||||
menuEntry = new SwingMenuEntryItem(SwingMenu.this, callback);
|
if (!menuText.equals("AAAAAAAA")) {
|
||||||
menuEntry.setText(menuText);
|
menuEntry = new SwingMenuEntryItem(SwingMenu.this, callback);
|
||||||
menuEntry.setImage(imagePath);
|
menuEntry.setText(menuText);
|
||||||
|
menuEntry.setImage(imagePath);
|
||||||
|
} else {
|
||||||
|
menuEntry = new SwingMenu(getSystemTray(), SwingMenu.this);
|
||||||
|
menuEntry.setText(menuText);
|
||||||
|
menuEntry.setImage(imagePath);
|
||||||
|
((SwingMenu) menuEntry).addMenuEntry("asdasdasd", null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
menuEntries.add(menuEntry);
|
menuEntries.add(menuEntry);
|
||||||
}
|
}
|
||||||
|
@ -102,4 +126,131 @@ class SwingMenu extends Menu {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// always called in the EDT
|
||||||
|
void renderText(final String text) {
|
||||||
|
((JMenuItem) _native).setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("Duplicates")
|
||||||
|
void setImage_(final File imageFile) {
|
||||||
|
hasLegitIcon = imageFile != null;
|
||||||
|
|
||||||
|
SwingUtil.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
if (imageFile != null) {
|
||||||
|
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
|
||||||
|
((JMenuItem) _native).setIcon(origIcon);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
((JMenuItem) _native).setIcon(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setText(final String newText) {
|
||||||
|
text = newText;
|
||||||
|
SwingUtil.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
renderText(newText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setImage(final File imageFile) {
|
||||||
|
if (imageFile == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final String imagePath) {
|
||||||
|
if (imagePath == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imagePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final URL imageUrl) {
|
||||||
|
if (imageUrl == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final String cacheName, final InputStream imageStream) {
|
||||||
|
if (imageStream == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, cacheName, imageStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void setImage(final InputStream imageStream) {
|
||||||
|
if (imageStream == null) {
|
||||||
|
setImage_(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImage_(ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, imageStream));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
boolean hasImage() {
|
||||||
|
return hasLegitIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void setCallback(final SystemTrayMenuAction callback) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final
|
||||||
|
void remove() {
|
||||||
|
SwingUtil.invokeAndWait(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public
|
||||||
|
void run() {
|
||||||
|
((SwingMenu) getParent())._native.remove(_native);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,24 +30,24 @@ abstract
|
||||||
class SwingMenuEntry implements MenuEntry {
|
class SwingMenuEntry implements MenuEntry {
|
||||||
private final int id = Menu.MENU_ID_COUNTER.getAndIncrement();
|
private final int id = Menu.MENU_ID_COUNTER.getAndIncrement();
|
||||||
|
|
||||||
private final SwingMenu parentMenu;
|
private final SwingMenu parent;
|
||||||
final JComponent _native;
|
final JComponent _native;
|
||||||
|
|
||||||
// this have to be volatile, because they can be changed from any thread
|
// this have to be volatile, because they can be changed from any thread
|
||||||
private volatile String text;
|
private volatile String text;
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
SwingMenuEntry(final SwingMenu parentMenu, final JComponent menuItem) {
|
SwingMenuEntry(final SwingMenu parent, final JComponent menuItem) {
|
||||||
this.parentMenu = parentMenu;
|
this.parent = parent;
|
||||||
this._native = menuItem;
|
this._native = menuItem;
|
||||||
|
|
||||||
parentMenu._native.add(menuItem);
|
parent._native.add(menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
Menu getParent() {
|
Menu getParent() {
|
||||||
return parentMenu;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,7 +143,7 @@ class SwingMenuEntry implements MenuEntry {
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
removePrivate();
|
removePrivate();
|
||||||
parentMenu._native.remove(_native);
|
parent._native.remove(_native);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,8 +33,8 @@ class SwingMenuEntryItem extends SwingMenuEntry {
|
||||||
private volatile SystemTrayMenuAction callback;
|
private volatile SystemTrayMenuAction callback;
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
SwingMenuEntryItem(final SwingMenu parentMenu, final SystemTrayMenuAction callback) {
|
SwingMenuEntryItem(final SwingMenu parent, final SystemTrayMenuAction callback) {
|
||||||
super(parentMenu, new AdjustedJMenuItem());
|
super(parent, new AdjustedJMenuItem());
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,6 +86,7 @@ class SwingMenuEntryItem extends SwingMenuEntry {
|
||||||
((JMenuItem) _native).setText(text);
|
((JMenuItem) _native).setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("Duplicates")
|
||||||
@Override
|
@Override
|
||||||
void setImage_(final File imageFile) {
|
void setImage_(final File imageFile) {
|
||||||
hasLegitIcon = imageFile != null;
|
hasLegitIcon = imageFile != null;
|
||||||
|
|
|
@ -25,8 +25,8 @@ import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
class SwingMenuEntrySpacer extends SwingMenuEntry implements MenuSpacer {
|
class SwingMenuEntrySpacer extends SwingMenuEntry implements MenuSpacer {
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
SwingMenuEntrySpacer(final SwingMenu parentMenu) {
|
SwingMenuEntrySpacer(final SwingMenu parent) {
|
||||||
super(parentMenu, new JSeparator(JSeparator.HORIZONTAL));
|
super(parent, new JSeparator(JSeparator.HORIZONTAL));
|
||||||
}
|
}
|
||||||
|
|
||||||
// called in the EDT thread
|
// called in the EDT thread
|
||||||
|
|
|
@ -26,8 +26,8 @@ import dorkbox.systemTray.SystemTrayMenuAction;
|
||||||
class SwingMenuEntryStatus extends SwingMenuEntry implements MenuStatus {
|
class SwingMenuEntryStatus extends SwingMenuEntry implements MenuStatus {
|
||||||
|
|
||||||
// this is ALWAYS called on the EDT.
|
// this is ALWAYS called on the EDT.
|
||||||
SwingMenuEntryStatus(final SwingMenu parentMenu, final String label) {
|
SwingMenuEntryStatus(final SwingMenu parent, final String label) {
|
||||||
super(parentMenu, new JMenuItem());
|
super(parent, new JMenuItem());
|
||||||
setText(label);
|
setText(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,6 @@ class SwingSystemTray extends SwingMenu {
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
|
@ -56,6 +54,9 @@ class SwingSystemTray extends SwingMenu {
|
||||||
SwingSystemTray(final dorkbox.systemTray.SystemTray systemTray) {
|
SwingSystemTray(final dorkbox.systemTray.SystemTray systemTray) {
|
||||||
super(systemTray, null);
|
super(systemTray, null);
|
||||||
|
|
||||||
|
// have to reassign our type...
|
||||||
|
_native = new SwingSystemTrayMenuPopup();
|
||||||
|
|
||||||
ImageUtils.determineIconSize(dorkbox.systemTray.SystemTray.TYPE_SWING);
|
ImageUtils.determineIconSize(dorkbox.systemTray.SystemTray.TYPE_SWING);
|
||||||
|
|
||||||
SwingUtil.invokeAndWait(new Runnable() {
|
SwingUtil.invokeAndWait(new Runnable() {
|
||||||
|
@ -143,10 +144,8 @@ class SwingSystemTray extends SwingMenu {
|
||||||
final Image trayImage = new ImageIcon(iconFile.getAbsolutePath()).getImage();
|
final Image trayImage = new ImageIcon(iconFile.getAbsolutePath()).getImage();
|
||||||
trayImage.flush();
|
trayImage.flush();
|
||||||
|
|
||||||
if (!isActive) {
|
if (trayIcon == null) {
|
||||||
// here we init. everything
|
// here we init. everything
|
||||||
isActive = true;
|
|
||||||
|
|
||||||
trayIcon = new TrayIcon(trayImage);
|
trayIcon = new TrayIcon(trayImage);
|
||||||
|
|
||||||
// appindicators DO NOT support anything other than PLAIN gtk-menus
|
// appindicators DO NOT support anything other than PLAIN gtk-menus
|
||||||
|
@ -185,7 +184,7 @@ class SwingSystemTray extends SwingMenu {
|
||||||
|
|
||||||
// voodoo to get this to popup to have the correct parent
|
// voodoo to get this to popup to have the correct parent
|
||||||
// from: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6285881
|
// from: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6285881
|
||||||
_native.setInvoker(_native);
|
((SwingSystemTrayMenuPopup) _native).setInvoker(_native);
|
||||||
_native.setLocation(x, y);
|
_native.setLocation(x, y);
|
||||||
_native.setVisible(true);
|
_native.setVisible(true);
|
||||||
_native.setFocusable(true);
|
_native.setFocusable(true);
|
||||||
|
@ -194,7 +193,7 @@ class SwingSystemTray extends SwingMenu {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SwingSystemTray.this.tray.add(trayIcon);
|
tray.add(trayIcon);
|
||||||
} catch (AWTException e) {
|
} catch (AWTException e) {
|
||||||
dorkbox.systemTray.SystemTray.logger.error("TrayIcon could not be added.", e);
|
dorkbox.systemTray.SystemTray.logger.error("TrayIcon could not be added.", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class SwingSystemTrayMenuPopup extends JPopupMenu {
|
||||||
|
|
||||||
private DelayTimer timer;
|
private DelayTimer timer;
|
||||||
|
|
||||||
protected volatile Point previousLocation = null;
|
protected volatile Point mouseClickLocation = null;
|
||||||
|
|
||||||
// protected boolean mouseStillOnMenu;
|
// protected boolean mouseStillOnMenu;
|
||||||
// private JDialog hiddenDialog;
|
// private JDialog hiddenDialog;
|
||||||
|
@ -77,9 +77,9 @@ class SwingSystemTrayMenuPopup extends JPopupMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
// has the mouse pointer moved > delta pixels from it's original location (when the tray icon was clicked)?
|
// has the mouse pointer moved > delta pixels from it's original location (when the tray icon was clicked)?
|
||||||
else if (previousLocation != null &&
|
else if (mouseClickLocation != null &&
|
||||||
location.x >= previousLocation.x - MOVEMENT_DELTA && location.x < previousLocation.x + MOVEMENT_DELTA &&
|
location.x >= mouseClickLocation.x - MOVEMENT_DELTA && location.x < mouseClickLocation.x + MOVEMENT_DELTA &&
|
||||||
location.y >= previousLocation.y - MOVEMENT_DELTA && location.y < previousLocation.y + MOVEMENT_DELTA) {
|
location.y >= mouseClickLocation.y - MOVEMENT_DELTA && location.y < mouseClickLocation.y + MOVEMENT_DELTA) {
|
||||||
|
|
||||||
// restart the timer
|
// restart the timer
|
||||||
SwingSystemTrayMenuPopup.this.timer.delay(POPUP_HIDE_DELAY);
|
SwingSystemTrayMenuPopup.this.timer.delay(POPUP_HIDE_DELAY);
|
||||||
|
@ -125,10 +125,11 @@ class SwingSystemTrayMenuPopup extends JPopupMenu {
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setVisible(boolean makeVisible) {
|
void setVisible(boolean makeVisible) {
|
||||||
|
// only allow java to close this popup if our timer closed it
|
||||||
this.timer.cancel();
|
this.timer.cancel();
|
||||||
|
|
||||||
if (makeVisible) {
|
if (makeVisible) {
|
||||||
previousLocation = MouseInfo.getPointerInfo().getLocation();
|
mouseClickLocation = MouseInfo.getPointerInfo().getLocation();
|
||||||
|
|
||||||
// if the mouse isn't inside the popup in x seconds, close the popup
|
// if the mouse isn't inside the popup in x seconds, close the popup
|
||||||
this.timer.delay(POPUP_HIDE_DELAY);
|
this.timer.delay(POPUP_HIDE_DELAY);
|
||||||
|
|
Loading…
Reference in New Issue