WIP swing submenus

This commit is contained in:
nathan 2016-09-29 03:00:29 +02:00
parent 7509d7c960
commit b354daab68
7 changed files with 182 additions and 30 deletions

View File

@ -17,17 +17,30 @@ package dorkbox.systemTray.swing;
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.MenuEntry;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.SystemTrayMenuAction;
import dorkbox.systemTray.util.ImageUtils;
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
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
@ -43,7 +56,11 @@ class SwingMenu extends Menu {
@Override
public
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 {
// must always be called on the EDT
menuEntry = new SwingMenuEntryItem(SwingMenu.this, callback);
menuEntry.setText(menuText);
menuEntry.setImage(imagePath);
if (!menuText.equals("AAAAAAAA")) {
menuEntry = new SwingMenuEntryItem(SwingMenu.this, callback);
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);
}
@ -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);
}
});
}
}

View File

@ -30,24 +30,24 @@ abstract
class SwingMenuEntry implements MenuEntry {
private final int id = Menu.MENU_ID_COUNTER.getAndIncrement();
private final SwingMenu parentMenu;
private final SwingMenu parent;
final JComponent _native;
// this have to be volatile, because they can be changed from any thread
private volatile String text;
// this is ALWAYS called on the EDT.
SwingMenuEntry(final SwingMenu parentMenu, final JComponent menuItem) {
this.parentMenu = parentMenu;
SwingMenuEntry(final SwingMenu parent, final JComponent menuItem) {
this.parent = parent;
this._native = menuItem;
parentMenu._native.add(menuItem);
parent._native.add(menuItem);
}
@Override
public
Menu getParent() {
return parentMenu;
return parent;
}
/**
@ -143,7 +143,7 @@ class SwingMenuEntry implements MenuEntry {
public
void run() {
removePrivate();
parentMenu._native.remove(_native);
parent._native.remove(_native);
}
});
}

View File

@ -33,8 +33,8 @@ class SwingMenuEntryItem extends SwingMenuEntry {
private volatile SystemTrayMenuAction callback;
// this is ALWAYS called on the EDT.
SwingMenuEntryItem(final SwingMenu parentMenu, final SystemTrayMenuAction callback) {
super(parentMenu, new AdjustedJMenuItem());
SwingMenuEntryItem(final SwingMenu parent, final SystemTrayMenuAction callback) {
super(parent, new AdjustedJMenuItem());
this.callback = callback;
@ -86,6 +86,7 @@ class SwingMenuEntryItem extends SwingMenuEntry {
((JMenuItem) _native).setText(text);
}
@SuppressWarnings("Duplicates")
@Override
void setImage_(final File imageFile) {
hasLegitIcon = imageFile != null;

View File

@ -25,8 +25,8 @@ import dorkbox.systemTray.SystemTrayMenuAction;
class SwingMenuEntrySpacer extends SwingMenuEntry implements MenuSpacer {
// this is ALWAYS called on the EDT.
SwingMenuEntrySpacer(final SwingMenu parentMenu) {
super(parentMenu, new JSeparator(JSeparator.HORIZONTAL));
SwingMenuEntrySpacer(final SwingMenu parent) {
super(parent, new JSeparator(JSeparator.HORIZONTAL));
}
// called in the EDT thread

View File

@ -26,8 +26,8 @@ import dorkbox.systemTray.SystemTrayMenuAction;
class SwingMenuEntryStatus extends SwingMenuEntry implements MenuStatus {
// this is ALWAYS called on the EDT.
SwingMenuEntryStatus(final SwingMenu parentMenu, final String label) {
super(parentMenu, new JMenuItem());
SwingMenuEntryStatus(final SwingMenu parent, final String label) {
super(parent, new JMenuItem());
setText(label);
}

View File

@ -47,8 +47,6 @@ class SwingSystemTray extends SwingMenu {
volatile SystemTray tray;
volatile TrayIcon trayIcon;
volatile boolean isActive = false;
/**
* Creates a new system tray handler class.
*/
@ -56,6 +54,9 @@ class SwingSystemTray extends SwingMenu {
SwingSystemTray(final dorkbox.systemTray.SystemTray systemTray) {
super(systemTray, null);
// have to reassign our type...
_native = new SwingSystemTrayMenuPopup();
ImageUtils.determineIconSize(dorkbox.systemTray.SystemTray.TYPE_SWING);
SwingUtil.invokeAndWait(new Runnable() {
@ -143,10 +144,8 @@ class SwingSystemTray extends SwingMenu {
final Image trayImage = new ImageIcon(iconFile.getAbsolutePath()).getImage();
trayImage.flush();
if (!isActive) {
if (trayIcon == null) {
// here we init. everything
isActive = true;
trayIcon = new TrayIcon(trayImage);
// 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
// from: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6285881
_native.setInvoker(_native);
((SwingSystemTrayMenuPopup) _native).setInvoker(_native);
_native.setLocation(x, y);
_native.setVisible(true);
_native.setFocusable(true);
@ -194,7 +193,7 @@ class SwingSystemTray extends SwingMenu {
});
try {
SwingSystemTray.this.tray.add(trayIcon);
tray.add(trayIcon);
} catch (AWTException e) {
dorkbox.systemTray.SystemTray.logger.error("TrayIcon could not be added.", e);
}

View File

@ -42,7 +42,7 @@ class SwingSystemTrayMenuPopup extends JPopupMenu {
private DelayTimer timer;
protected volatile Point previousLocation = null;
protected volatile Point mouseClickLocation = null;
// protected boolean mouseStillOnMenu;
// 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)?
else if (previousLocation != null &&
location.x >= previousLocation.x - MOVEMENT_DELTA && location.x < previousLocation.x + MOVEMENT_DELTA &&
location.y >= previousLocation.y - MOVEMENT_DELTA && location.y < previousLocation.y + MOVEMENT_DELTA) {
else if (mouseClickLocation != null &&
location.x >= mouseClickLocation.x - MOVEMENT_DELTA && location.x < mouseClickLocation.x + MOVEMENT_DELTA &&
location.y >= mouseClickLocation.y - MOVEMENT_DELTA && location.y < mouseClickLocation.y + MOVEMENT_DELTA) {
// restart the timer
SwingSystemTrayMenuPopup.this.timer.delay(POPUP_HIDE_DELAY);
@ -125,10 +125,11 @@ class SwingSystemTrayMenuPopup extends JPopupMenu {
@Override
public
void setVisible(boolean makeVisible) {
// only allow java to close this popup if our timer closed it
this.timer.cancel();
if (makeVisible) {
previousLocation = MouseInfo.getPointerInfo().getLocation();
mouseClickLocation = MouseInfo.getPointerInfo().getLocation();
// if the mouse isn't inside the popup in x seconds, close the popup
this.timer.delay(POPUP_HIDE_DELAY);