Code cleanup, GTK.dispatchAndWait refactor, fixed issue with hiding

the tray icon in MacOSx.
This commit is contained in:
nathan 2016-10-16 15:58:02 +02:00
parent e52591a388
commit a26522411e
9 changed files with 117 additions and 59 deletions

View File

@ -672,12 +672,13 @@ class SystemTray implements Menu {
}
/**
* Shuts-down the SystemTray, by removing the menus + tray icon.
* Shuts-down the SystemTray, by removing the menus + tray icon. After calling this method, you MUST call `get()` or `getNative()`
* again to obtain a new reference to the SystemTray.
*/
public
void shutdown() {
// this will call "dispatchAndWait()" behind the scenes, so it is thread-safe
final Menu menu = systemTrayMenu;
if (menu instanceof _AppIndicatorTray) {
((_AppIndicatorTray) menu).shutdown();
}
@ -693,7 +694,7 @@ class SystemTray implements Menu {
else if (menu instanceof _AwtTray) {
((_AwtTray) menu).shutdown();
}
else {
else if (menu instanceof _SwingTray) {
((_SwingTray) menu).shutdown();
}
systemTrayMenu = null;
@ -721,9 +722,12 @@ class SystemTray implements Menu {
else if (menu instanceof _AwtTray) {
return ((_AwtTray) menu).getStatus();
}
else {
else if (menu instanceof _SwingTray) {
return ((_SwingTray) menu).getStatus();
}
else {
return "";
}
}
/**
@ -750,7 +754,7 @@ class SystemTray implements Menu {
else if (menu instanceof _AwtTray) {
((_AwtTray) menu).setStatus(statusText);
}
else {
else if (menu instanceof _SwingTray) {
((_SwingTray) menu).setStatus(statusText);
}
}

View File

@ -57,6 +57,8 @@ class Gtk {
// there is ONLY a single thread EVER setting this value!!
private static volatile boolean isDispatch = false;
private static final int TIMEOUT = 2;
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk
// objdump -T /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 | grep gtk
@ -363,9 +365,44 @@ class Gtk {
}
}
public static
void dispatchAndWait(final Runnable runnable) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
Gtk.dispatch(new Runnable() {
@Override
public
void run() {
try {
runnable.run();
} finally {
countDownLatch.countDown();
}
}
});
// this is slightly different than how swing does it. We have a timeout here so that we can make sure that updates on the GUI
// thread occur in REASONABLE time-frames, and alert the user if not.
try {
if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " +
"`SystemTray.TIMEOUT` to a value which better suites your environment.");
} else {
throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " +
"`SystemTray.TIMEOUT` to a value which better suites your environment.");
}
}
} catch (InterruptedException e) {
SystemTray.logger.error("Error waiting for dispatch to complete.", new Exception());
}
}
public static
void shutdownGui() {
dispatch(new Runnable() {
dispatchAndWait(new Runnable() {
@Override
public
void run() {

View File

@ -19,8 +19,6 @@ package dorkbox.systemTray.nativeUI;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import com.sun.jna.Pointer;
@ -34,8 +32,6 @@ import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.util.MenuBase;
class GtkMenu extends MenuBase implements NativeUI {
static int TIMEOUT = 2;
// menu entry that this menu is attached to. Will be NULL when it's the system tray
private final GtkEntryItem menuEntry;
@ -79,35 +75,7 @@ class GtkMenu extends MenuBase implements NativeUI {
*/
protected
void dispatchAndWait(final Runnable runnable) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
Gtk.dispatch(new Runnable() {
@Override
public
void run() {
try {
runnable.run();
} finally {
countDownLatch.countDown();
}
}
});
// this is slightly different than how swing does it. We have a timeout here so that we can make sure that updates on the GUI
// thread occur in REASONABLE time-frames, and alert the user if not.
try {
if (!countDownLatch.await(TIMEOUT, TimeUnit.SECONDS)) {
if (SystemTray.DEBUG) {
SystemTray.logger.error("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " +
"`SystemTray.TIMEOUT` to a value which better suites your environment.");
} else {
throw new RuntimeException("Event dispatch queue took longer than " + TIMEOUT + " seconds to complete. Please adjust " +
"`SystemTray.TIMEOUT` to a value which better suites your environment.");
}
}
} catch (InterruptedException e) {
SystemTray.logger.error("Error waiting for dispatch to complete.", new Exception());
}
Gtk.dispatchAndWait(runnable);
}
public
@ -117,10 +85,11 @@ class GtkMenu extends MenuBase implements NativeUI {
public
void run() {
obliterateMenu();
Gtk.shutdownGui();
}
});
// does not need to be called on the dispatch (it does that)
Gtk.shutdownGui();
}
// public here so that Swing/Gtk/AppIndicator can access this
@ -314,7 +283,7 @@ class GtkMenu extends MenuBase implements NativeUI {
// will also get: gsignal.c:2516: signal 'child-added' is invalid for instance '0x7f1df8244080' of type 'GtkMenu'
Gtk.gtk_menu_shell_append(this._native, entry._native);
Gobject.g_object_ref_sink(entry._native); // undoes "floating"
Gtk.gtk_widget_show_all(entry._native);
Gtk.gtk_widget_show_all(entry._native); // necessary to guarantee widget is visible
}
else if (menuEntry__ instanceof GtkMenu) {
GtkMenu subMenu = (GtkMenu) menuEntry__;
@ -322,7 +291,7 @@ class GtkMenu extends MenuBase implements NativeUI {
// will also get: gsignal.c:2516: signal 'child-added' is invalid for instance '0x7f1df8244080' of type 'GtkMenu'
Gtk.gtk_menu_shell_append(this._native, subMenu.menuEntry._native);
Gobject.g_object_ref_sink(subMenu.menuEntry._native); // undoes "floating"
Gtk.gtk_widget_show_all(subMenu.menuEntry._native);
Gtk.gtk_widget_show_all(subMenu.menuEntry._native); // necessary to guarantee widget is visible
if (subMenu.getParent() != GtkMenu.this) {
// we don't want to "createMenu" on our sub-menu that is assigned to us directly, as they are already doing it
@ -332,7 +301,7 @@ class GtkMenu extends MenuBase implements NativeUI {
}
onMenuAdded(_native);
Gtk.gtk_widget_show_all(_native);
Gtk.gtk_widget_show_all(_native); // necessary to guarantee widget is visible (doesn't always show_all for all children)
}
}

View File

@ -157,8 +157,6 @@ class _AppIndicatorNativeTray extends GtkMenu {
@Override
public final
void setEnabled(final boolean setEnabled) {
visible = !setEnabled;
Gtk.dispatch(new Runnable() {
@Override
public
@ -166,9 +164,11 @@ class _AppIndicatorNativeTray extends GtkMenu {
if (visible && !setEnabled) {
// STATUS_PASSIVE hides the indicator
AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_PASSIVE);
visible = false;
}
else if (!visible && setEnabled) {
AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_ACTIVE);
visible = true;
}
}
});

View File

@ -24,8 +24,11 @@ import java.io.File;
import javax.swing.ImageIcon;
import dorkbox.util.OS;
/**
* Class for handling all system tray interaction, via AWT.
* Class for handling all system tray interaction, via AWT. Pretty much EXCLUSIVELY for on MacOS, because that is the only time this
* looks good
*
* It doesn't work well on linux. See bugs:
* http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6267936
@ -41,6 +44,9 @@ class _AwtTray extends AwtMenu {
// is the system tray visible or not.
private volatile boolean visible = true;
private final Object keepAliveLock = new Object[0];
private Thread keepAliveThread;
// Called in the EDT
public
_AwtTray(final dorkbox.systemTray.SystemTray systemTray) {
@ -56,7 +62,7 @@ class _AwtTray extends AwtMenu {
public
void shutdown() {
dispatch(new Runnable() {
dispatchAndWait(new Runnable() {
@Override
public
void run() {
@ -103,7 +109,42 @@ class _AwtTray extends AwtMenu {
@SuppressWarnings("Duplicates")
public
void setEnabled(final boolean setEnabled) {
visible = !setEnabled;
if (OS.isMacOsX()) {
if (keepAliveThread != null) {
synchronized (keepAliveLock) {
keepAliveLock.notifyAll();
}
}
keepAliveThread = null;
if (visible && !setEnabled) {
// THIS WILL NOT keep the app running, so we use a "keep-alive" thread so this behavior is THE SAME across
// all platforms. This was only noticed on MacOS (where the app would quit after calling setEnabled(false);
keepAliveThread = new Thread(new Runnable() {
@Override
public
void run() {
synchronized (keepAliveLock) {
keepAliveLock.notifyAll();
try {
keepAliveLock.wait();
} catch (InterruptedException ignored) {
}
}
}
}, "KeepAliveThread");
keepAliveThread.start();
}
synchronized (keepAliveLock) {
try {
keepAliveLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
dispatch(new Runnable() {
@Override
@ -111,10 +152,12 @@ class _AwtTray extends AwtMenu {
void run() {
if (visible && !setEnabled) {
tray.remove(trayIcon);
visible = false;
}
else if (!visible && setEnabled) {
try {
tray.add(trayIcon);
visible = true;
} catch (AWTException e) {
dorkbox.systemTray.SystemTray.logger.error("Error adding the icon back to the tray");
}

View File

@ -169,16 +169,16 @@ class _GtkStatusIconNativeTray extends GtkMenu {
@Override
public final
void setEnabled(final boolean setEnabled) {
visible = !setEnabled;
Gtk.dispatch(new Runnable() {
@Override
public
void run() {
if (visible && !setEnabled) {
Gtk.gtk_status_icon_set_visible(trayIcon, setEnabled);
visible = false;
} else if (!visible && setEnabled) {
Gtk.gtk_status_icon_set_visible(trayIcon, setEnabled);
visible = true;
}
}
});

View File

@ -213,6 +213,7 @@ class _AppIndicatorTray extends SwingMenu {
}
});
// does not need to be called on the dispatch (it does that)
Gtk.shutdownGui();
// uses EDT
@ -230,7 +231,7 @@ class _AppIndicatorTray extends SwingMenu {
@Override
public final
void setImage_(final File imageFile) {
dispatch(new Runnable() {
Gtk.dispatch(new Runnable() {
@Override
public
void run() {
@ -247,6 +248,8 @@ class _AppIndicatorTray extends SwingMenu {
}
});
// needs to be on EDT
dispatch(new Runnable() {
@Override
public
@ -259,8 +262,6 @@ class _AppIndicatorTray extends SwingMenu {
@Override
public final
void setEnabled(final boolean setEnabled) {
visible = !setEnabled;
Gtk.dispatch(new Runnable() {
@Override
public
@ -268,9 +269,11 @@ class _AppIndicatorTray extends SwingMenu {
if (visible && !setEnabled) {
// STATUS_PASSIVE hides the indicator
AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_PASSIVE);
visible = false;
}
else if (!visible && setEnabled) {
AppIndicator.app_indicator_set_status(appIndicator, AppIndicator.STATUS_ACTIVE);
visible = true;
}
}
});

View File

@ -162,6 +162,7 @@ class _GtkStatusIconTray extends SwingMenu {
}
});
// does not need to be called on the dispatch (it does that)
Gtk.shutdownGui();
// uses EDT
@ -185,6 +186,7 @@ class _GtkStatusIconTray extends SwingMenu {
}
});
// needs to be on EDT
dispatch(new Runnable() {
@Override
public
@ -196,16 +198,16 @@ class _GtkStatusIconTray extends SwingMenu {
public
void setEnabled(final boolean setEnabled) {
visible = !setEnabled;
Gtk.dispatch(new Runnable() {
@Override
public
void run() {
if (visible && !setEnabled) {
Gtk.gtk_status_icon_set_visible(trayIcon, setEnabled);
visible = false;
} else if (!visible && setEnabled) {
Gtk.gtk_status_icon_set_visible(trayIcon, setEnabled);
visible = true;
}
}
});

View File

@ -118,20 +118,20 @@ class _SwingTray extends SwingMenu {
@SuppressWarnings("Duplicates")
public
void setEnabled(final boolean setEnabled) {
visible = !setEnabled;
dispatch(new Runnable() {
@Override
public
void run() {
if (visible && !setEnabled) {
tray.remove(trayIcon);
visible = false;
}
else if (!visible && setEnabled) {
try {
tray.add(trayIcon);
visible = true;
} catch (AWTException e) {
dorkbox.systemTray.SystemTray.logger.error("Error adding the icon back to the tray");
dorkbox.systemTray.SystemTray.logger.error("Error adding the icon back to the tray", e);
}
}
}