Fixed issues with spacer images, fixed issues with adding images to a
menu entry after adding it to a menu. Removed GTK event dispatch queue, properly fixed out-of-order menu creation. During entry removal, the native peer takes care of cleaning up the native bits.
This commit is contained in:
parent
740348b6c4
commit
e1cca820c3
|
@ -81,7 +81,7 @@ class Entry {
|
|||
}
|
||||
|
||||
/**
|
||||
* Removes this menu entry from the menu and releases all system resources associated with this menu entry
|
||||
* Removes this menu entry from the menu and releases all system resources associated with this menu entry.
|
||||
*/
|
||||
public
|
||||
void remove() {
|
||||
|
|
|
@ -408,33 +408,15 @@ class Menu extends MenuItem {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This removes all menu entries from this menu
|
||||
*/
|
||||
public
|
||||
void clear() {
|
||||
List<Entry> copy;
|
||||
synchronized (menuEntries) {
|
||||
// access on this object must be synchronized for object visibility
|
||||
// a copy is made to prevent deadlocks from occurring when operating in different threads
|
||||
// have to make copy because we are deleting all of them, and sub-menus remove themselves from parents
|
||||
copy = new ArrayList<Entry>(menuEntries);
|
||||
menuEntries.clear();
|
||||
}
|
||||
|
||||
for (Entry entry : copy) {
|
||||
entry.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This removes all menu entries from this menu AND this menu from it's parent
|
||||
*/
|
||||
@Override
|
||||
public
|
||||
void remove() {
|
||||
clear();
|
||||
synchronized (menuEntries) {
|
||||
menuEntries.clear();
|
||||
}
|
||||
|
||||
super.remove();
|
||||
}
|
||||
|
|
|
@ -387,16 +387,4 @@ class MenuItem extends Entry {
|
|||
String getTooltip() {
|
||||
return this.tooltip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void remove() {
|
||||
if (peer != null) {
|
||||
setImage_(null);
|
||||
setText(null);
|
||||
setCallback(null);
|
||||
}
|
||||
|
||||
super.remove();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import dorkbox.systemTray.ui.gtk._AppIndicatorNativeTray;
|
|||
import dorkbox.systemTray.ui.gtk._GtkStatusIconNativeTray;
|
||||
import dorkbox.systemTray.ui.swing.SwingUIFactory;
|
||||
import dorkbox.systemTray.ui.swing._SwingTray;
|
||||
import dorkbox.systemTray.util.EventDispatch;
|
||||
import dorkbox.systemTray.util.ImageResizeUtil;
|
||||
import dorkbox.systemTray.util.LinuxSwingUI;
|
||||
import dorkbox.systemTray.util.SizeAndScalingUtil;
|
||||
|
@ -226,7 +227,7 @@ class SystemTray {
|
|||
OSUtil.DesktopEnv.Env de = OSUtil.DesktopEnv.get();
|
||||
|
||||
if (DEBUG) {
|
||||
logger.debug("Currently using the '{}' desktop environment", de);
|
||||
logger.debug("Currently using the '{}' desktop environment" + OS.LINE_SEPARATOR + OSUtil.Linux.getInfo(), de);
|
||||
}
|
||||
|
||||
switch (de) {
|
||||
|
@ -310,6 +311,19 @@ class SystemTray {
|
|||
// kde 5.8+ is "high DPI", so we need to adjust the scale. Image resize will do that
|
||||
}
|
||||
case Unity: {
|
||||
try {
|
||||
String ubuntuVersion = OSUtil.Linux.getUbuntuVersion();
|
||||
String[] split = ubuntuVersion.split(".");
|
||||
int major = Integer.parseInt(split[0]);
|
||||
int minor = Integer.parseInt(split[1]);
|
||||
|
||||
// <=16.04 it is better to use GtkStatusIcons.
|
||||
if (major < 16 || (major == 16 && minor <= 4)) {
|
||||
return selectTypeQuietly(TrayType.GtkStatusIcon);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// Ubuntu Unity is a weird combination. It's "Gnome", but it's not "Gnome Shell".
|
||||
return selectTypeQuietly(TrayType.AppIndicator);
|
||||
}
|
||||
|
@ -393,6 +407,19 @@ class SystemTray {
|
|||
logger.error("Error detecting gnome version", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (OS.isLinux()) {
|
||||
// now just blanket query what we are to guess...
|
||||
if (OSUtil.Linux.isUbuntu()) {
|
||||
return selectTypeQuietly(TrayType.AppIndicator);
|
||||
}
|
||||
else if (OSUtil.Linux.isFedora()) {
|
||||
return selectTypeQuietly(TrayType.AppIndicator);
|
||||
} else {
|
||||
// AppIndicators are now the "default" for most linux distro's.
|
||||
return selectTypeQuietly(TrayType.AppIndicator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -1025,13 +1052,14 @@ class SystemTray {
|
|||
*/
|
||||
public
|
||||
void shutdown() {
|
||||
// this will call "dispatchAndWait()" behind the scenes, so it is thread-safe
|
||||
// this is thread-safe
|
||||
final Menu menu = systemTrayMenu;
|
||||
if (menu != null) {
|
||||
menu.remove();
|
||||
}
|
||||
|
||||
systemTrayMenu = null;
|
||||
EventDispatch.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,7 +30,7 @@ abstract
|
|||
class GtkBaseMenuItem implements EntryPeer {
|
||||
// these are necessary BECAUSE GTK menus look funky as hell when there are some menu entries WITH icons and some WITHOUT
|
||||
private static final File transparentIcon = ImageResizeUtil.getTransparentImage();
|
||||
private volatile boolean hasLegitImage = true;
|
||||
private volatile boolean hasLegitImage = false; // default is to not have an image assigned
|
||||
|
||||
// these have to be volatile, because they can be changed from any thread
|
||||
private volatile Pointer spacerImage;
|
||||
|
@ -51,6 +51,39 @@ class GtkBaseMenuItem implements EntryPeer {
|
|||
hasLegitImage = isLegit;
|
||||
}
|
||||
|
||||
/**
|
||||
* always remove a spacer image.
|
||||
* <p>
|
||||
* called on the DISPATCH thread
|
||||
*/
|
||||
protected
|
||||
void removeSpacerImage() {
|
||||
if (spacerImage != null) {
|
||||
Gtk2.gtk_container_remove(_native, spacerImage); // will automatically get destroyed if no other references to it
|
||||
spacerImage = null;
|
||||
Gtk2.gtk_widget_show_all(_native);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* always add a spacer image.
|
||||
* <p>
|
||||
* called on the DISPATCH thread
|
||||
*/
|
||||
protected
|
||||
void addSpacerImage() {
|
||||
if (spacerImage == null) {
|
||||
spacerImage = Gtk2.gtk_image_new_from_file(transparentIcon.getAbsolutePath());
|
||||
Gtk2.gtk_image_menu_item_set_image(_native, spacerImage);
|
||||
|
||||
// must always re-set always-show after setting the image
|
||||
Gtk2.gtk_image_menu_item_set_always_show_image(_native, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* the menu entry looks FUNKY when there are a mis-match of entries WITH and WITHOUT images.
|
||||
* This is primarily only with AppIndicators, although not always.
|
||||
|
@ -64,18 +97,10 @@ class GtkBaseMenuItem implements EntryPeer {
|
|||
return;
|
||||
}
|
||||
|
||||
if (spacerImage != null) {
|
||||
Gtk2.gtk_container_remove(_native, spacerImage); // will automatically get destroyed if no other references to it
|
||||
spacerImage = null;
|
||||
Gtk2.gtk_widget_show_all(_native);
|
||||
}
|
||||
removeSpacerImage();
|
||||
|
||||
if (everyoneElseHasImages) {
|
||||
spacerImage = Gtk2.gtk_image_new_from_file(transparentIcon.getAbsolutePath());
|
||||
Gtk2.gtk_image_menu_item_set_image(_native, spacerImage);
|
||||
|
||||
// must always re-set always-show after setting the image
|
||||
Gtk2.gtk_image_menu_item_set_always_show_image(_native, true);
|
||||
addSpacerImage();
|
||||
}
|
||||
|
||||
Gtk2.gtk_widget_show_all(_native);
|
||||
|
|
|
@ -197,7 +197,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||
@Override
|
||||
public
|
||||
void add(final Menu parentMenu, final Entry entry, final int index) {
|
||||
// must always be called on the GTK dispatch. This must be dispatchAndWait
|
||||
// must always be called on the GTK dispatch. This must be dispatchAndWait() so it will properly executed immediately
|
||||
GtkEventDispatch.dispatchAndWait(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
|
@ -206,35 +206,50 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||
// To work around this issue, we destroy then recreate the menu every time something is changed.
|
||||
deleteMenu();
|
||||
|
||||
GtkBaseMenuItem item = null;
|
||||
|
||||
if (entry instanceof Menu) {
|
||||
// some implementations of appindicator, do NOT like having a menu added, which has no menu items yet.
|
||||
// see: https://bugs.launchpad.net/glipper/+bug/1203888
|
||||
GtkMenu item = new GtkMenu(GtkMenu.this);
|
||||
item = new GtkMenu(GtkMenu.this);
|
||||
menuEntries.add(index, item);
|
||||
((Menu) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
else if (entry instanceof Separator) {
|
||||
GtkMenuItemSeparator item = new GtkMenuItemSeparator(GtkMenu.this);
|
||||
item = new GtkMenuItemSeparator(GtkMenu.this);
|
||||
menuEntries.add(index, item);
|
||||
entry.bind(item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
else if (entry instanceof Checkbox) {
|
||||
GtkMenuItemCheckbox item = new GtkMenuItemCheckbox(GtkMenu.this);
|
||||
item = new GtkMenuItemCheckbox(GtkMenu.this);
|
||||
menuEntries.add(index, item);
|
||||
((Checkbox) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
else if (entry instanceof Status) {
|
||||
GtkMenuItemStatus item = new GtkMenuItemStatus(GtkMenu.this);
|
||||
item = new GtkMenuItemStatus(GtkMenu.this);
|
||||
menuEntries.add(index, item);
|
||||
((Status) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
else if (entry instanceof MenuItem) {
|
||||
GtkMenuItem item = new GtkMenuItem(GtkMenu.this);
|
||||
item = new GtkMenuItem(GtkMenu.this);
|
||||
menuEntries.add(index, item);
|
||||
((MenuItem) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
|
||||
createMenu();
|
||||
|
||||
// we must create the menu BEFORE binding the menu, otherwise the menus' children's GTK element can be added before
|
||||
// their parent GTK elements are added (and the menu won't show up)
|
||||
if (entry instanceof Menu) {
|
||||
((Menu) entry).bind((GtkMenu) item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
else if (entry instanceof Separator) {
|
||||
((Separator)entry).bind((GtkMenuItemSeparator) item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
else if (entry instanceof Checkbox) {
|
||||
((Checkbox) entry).bind((GtkMenuItemCheckbox) item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
else if (entry instanceof Status) {
|
||||
((Status) entry).bind((GtkMenuItemStatus) item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
else if (entry instanceof MenuItem) {
|
||||
((MenuItem) entry).bind((GtkMenuItem) item, parentMenu, parentMenu.getSystemTray());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -252,7 +267,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||
// is overridden by system tray
|
||||
setLegitImage(menuItem.getImage() != null);
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
GtkEventDispatch.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
|
@ -263,8 +278,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||
}
|
||||
|
||||
if (menuItem.getImage() != null) {
|
||||
image = Gtk2.gtk_image_new_from_file(menuItem.getImage()
|
||||
.getAbsolutePath());
|
||||
image = Gtk2.gtk_image_new_from_file(menuItem.getImage().getAbsolutePath());
|
||||
Gtk2.gtk_image_menu_item_set_image(_native, image);
|
||||
|
||||
// must always re-set always-show after setting the image
|
||||
|
@ -273,14 +287,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||
|
||||
Gtk2.gtk_widget_show_all(_native);
|
||||
}
|
||||
};
|
||||
|
||||
if (GtkEventDispatch.isDispatch.get()) {
|
||||
runnable.run();
|
||||
}
|
||||
else {
|
||||
GtkEventDispatch.dispatch(runnable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// is overridden in tray impl
|
||||
|
@ -385,7 +392,7 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||
@Override
|
||||
public
|
||||
void remove() {
|
||||
Runnable runnable = new Runnable() {
|
||||
GtkEventDispatch.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
|
@ -408,13 +415,6 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||
parent.createMenu(); // must be on EDT
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (GtkEventDispatch.isDispatch.get()) {
|
||||
runnable.run();
|
||||
}
|
||||
else {
|
||||
GtkEventDispatch.dispatch(runnable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||
@Override
|
||||
public
|
||||
void setImage(final MenuItem menuItem) {
|
||||
final boolean hadImage = hasImage();
|
||||
setLegitImage(menuItem.getImage() != null);
|
||||
|
||||
GtkEventDispatch.dispatch(new Runnable() {
|
||||
|
@ -86,6 +87,9 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||
}
|
||||
|
||||
if (menuItem.getImage() != null) {
|
||||
// always remove the spacer image in case it's there. The spacer image will correctly added when the menu is created.
|
||||
removeSpacerImage();
|
||||
|
||||
image = Gtk2.gtk_image_new_from_file(menuItem.getImage()
|
||||
.getAbsolutePath());
|
||||
Gtk2.gtk_image_menu_item_set_image(_native, image);
|
||||
|
@ -93,6 +97,11 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||
// must always re-set always-show after setting the image
|
||||
Gtk2.gtk_image_menu_item_set_always_show_image(_native, true);
|
||||
}
|
||||
else if (hadImage) {
|
||||
// if at one point, we had an image, we should set the spacer image back, so that menu spacing looks correct.
|
||||
// since we USED to have an image, it is safe to assume that we should have a spacer image.
|
||||
addSpacerImage();
|
||||
}
|
||||
|
||||
Gtk2.gtk_widget_show_all(_native);
|
||||
}
|
||||
|
@ -205,14 +214,16 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||
@Override
|
||||
public
|
||||
void remove() {
|
||||
Runnable runnable = new Runnable() {
|
||||
GtkEventDispatch.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||
|
||||
GtkMenuItem.super.remove();
|
||||
|
||||
callback = null;
|
||||
|
||||
Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||
|
||||
if (image != null) {
|
||||
Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
|
||||
image = null;
|
||||
|
@ -220,13 +231,6 @@ class GtkMenuItem extends GtkBaseMenuItem implements MenuItemPeer, GCallback {
|
|||
|
||||
parent.remove(GtkMenuItem.this);
|
||||
}
|
||||
};
|
||||
|
||||
if (GtkEventDispatch.isDispatch.get()) {
|
||||
runnable.run();
|
||||
}
|
||||
else {
|
||||
GtkEventDispatch.dispatch(runnable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,14 +297,16 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||
@Override
|
||||
public
|
||||
void remove() {
|
||||
Runnable runnable = new Runnable() {
|
||||
GtkEventDispatch.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||
|
||||
GtkMenuItemCheckbox.super.remove();
|
||||
|
||||
callback = null;
|
||||
|
||||
Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||
|
||||
if (image != null) {
|
||||
Gtk2.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
|
||||
image = null;
|
||||
|
@ -312,13 +314,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||
|
||||
parent.remove(GtkMenuItemCheckbox.this);
|
||||
}
|
||||
};
|
||||
|
||||
if (GtkEventDispatch.isDispatch.get()) {
|
||||
runnable.run();
|
||||
}
|
||||
else {
|
||||
GtkEventDispatch.dispatch(runnable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ package dorkbox.systemTray.ui.gtk;
|
|||
|
||||
import static dorkbox.util.jna.linux.Gtk.Gtk2;
|
||||
|
||||
import dorkbox.systemTray.peer.EntryPeer;
|
||||
import dorkbox.systemTray.peer.SeparatorPeer;
|
||||
import dorkbox.util.jna.linux.GtkEventDispatch;
|
||||
|
||||
class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
|
||||
class GtkMenuItemSeparator extends GtkBaseMenuItem implements SeparatorPeer {
|
||||
|
||||
private final GtkMenu parent;
|
||||
|
||||
|
@ -37,7 +37,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
|
|||
@Override
|
||||
public
|
||||
void remove() {
|
||||
Runnable runnable = new Runnable() {
|
||||
GtkEventDispatch.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
|
@ -45,14 +45,7 @@ class GtkMenuItemSeparator extends GtkBaseMenuItem implements EntryPeer {
|
|||
|
||||
parent.remove(GtkMenuItemSeparator.this);
|
||||
}
|
||||
};
|
||||
|
||||
if (GtkEventDispatch.isDispatch.get()) {
|
||||
runnable.run();
|
||||
}
|
||||
else {
|
||||
GtkEventDispatch.dispatch(runnable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -61,23 +61,16 @@ class GtkMenuItemStatus extends GtkBaseMenuItem implements StatusPeer {
|
|||
@Override
|
||||
public
|
||||
void remove() {
|
||||
Runnable runnable = new Runnable() {
|
||||
GtkEventDispatch.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||
|
||||
GtkMenuItemStatus.super.remove();
|
||||
|
||||
Gtk2.gtk_container_remove(parent._nativeMenu, _native); // will automatically get destroyed if no other references to it
|
||||
|
||||
parent.remove(GtkMenuItemStatus.this);
|
||||
}
|
||||
};
|
||||
|
||||
if (GtkEventDispatch.isDispatch.get()) {
|
||||
runnable.run();
|
||||
}
|
||||
else {
|
||||
GtkEventDispatch.dispatch(runnable);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,25 +197,24 @@ class _AppIndicatorNativeTray extends Tray {
|
|||
void remove() {
|
||||
// This is required if we have JavaFX or SWT shutdown hooks (to prevent us from shutting down twice...)
|
||||
if (!shuttingDown.getAndSet(true)) {
|
||||
// must happen asap, so our hook properly notices we are in shutdown mode
|
||||
final AppIndicatorInstanceStruct savedAppIndicator = appIndicator;
|
||||
appIndicator = null;
|
||||
super.remove();
|
||||
|
||||
GtkEventDispatch.dispatch(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
// must happen asap, so our hook properly notices we are in shutdown mode
|
||||
final AppIndicatorInstanceStruct savedAppIndicator = appIndicator;
|
||||
appIndicator = null;
|
||||
|
||||
// STATUS_PASSIVE hides the indicator
|
||||
AppIndicator.app_indicator_set_status(savedAppIndicator, AppIndicator.STATUS_PASSIVE);
|
||||
Pointer p = savedAppIndicator.getPointer();
|
||||
Gobject.g_object_unref(p);
|
||||
|
||||
GtkEventDispatch.shutdownGui();
|
||||
}
|
||||
});
|
||||
|
||||
super.remove();
|
||||
|
||||
// does not need to be called on the dispatch (it does that)
|
||||
GtkEventDispatch.shutdownGui();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user