Fixed gtk entry/sub-menu removal for SWT. code polish

This commit is contained in:
nathan 2016-10-04 15:36:00 +02:00
parent b8cd5c875f
commit 62c73e8f32
7 changed files with 132 additions and 138 deletions

View File

@ -91,12 +91,6 @@ class Menu {
protected abstract protected abstract
Menu addMenu_(final String menuText, final File imagePath); Menu addMenu_(final String menuText, final File imagePath);
/*
* Called when this menu is removed from it's parent menu
*/
protected abstract
void removePrivate();
/* /*
* Necessary to guarantee all updates occur on the dispatch thread * Necessary to guarantee all updates occur on the dispatch thread
*/ */
@ -380,35 +374,7 @@ class Menu {
@Override @Override
public public
void run() { void run() {
try { remove__(menuEntry);
synchronized (menuEntries) {
for (Iterator<MenuEntry> iterator = menuEntries.iterator(); iterator.hasNext(); ) {
final MenuEntry entry = iterator.next();
if (entry == menuEntry) {
iterator.remove();
// this will also reset the menu
menuEntry.remove();
break;
}
}
// now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function.
if (!menuEntries.isEmpty()) {
if (menuEntries.get(0) instanceof MenuSpacer) {
remove(menuEntries.get(0));
}
}
// now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function.
if (!menuEntries.isEmpty()) {
if (menuEntries.get(menuEntries.size()-1) instanceof MenuSpacer) {
remove(menuEntries.get(menuEntries.size() - 1));
}
}
}
} catch (Exception e) {
SystemTray.logger.error("Error removing menu entry from list.", e);
}
} }
}); });
} }
@ -422,26 +388,34 @@ class Menu {
public public
void remove(final Menu menu) { void remove(final Menu menu) {
if (menu == null) { if (menu == null) {
throw new NullPointerException("No menu entry exists for menuEntry"); throw new NullPointerException("No sub-menu entry exists for menu");
} }
dispatchAndWait(new Runnable() { dispatchAndWait(new Runnable() {
@SuppressWarnings("Duplicates")
@Override @Override
public public
void run() { void run() {
remove__(menu);
}
});
}
protected
void remove__(final Object menuEntry) {
try { try {
synchronized (menuEntries) { synchronized (menuEntries) {
// null is passed in when a sub-menu is removing itself from us (because they have already called "remove" and have also
// removed themselves from the menuEntries)
if (menuEntry != null) {
for (Iterator<MenuEntry> iterator = menuEntries.iterator(); iterator.hasNext(); ) { for (Iterator<MenuEntry> iterator = menuEntries.iterator(); iterator.hasNext(); ) {
final MenuEntry entry = iterator.next(); final MenuEntry entry = iterator.next();
if (entry == menu) { if (entry == menuEntry) {
iterator.remove(); iterator.remove();
entry.remove();
// this will also reset the menu
menu.removePrivate();
break; break;
} }
} }
}
// now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function. // now check to see if a spacer is at the top/bottom of the list (and remove it if so. This is a recursive function.
if (!menuEntries.isEmpty()) { if (!menuEntries.isEmpty()) {
@ -457,11 +431,9 @@ class Menu {
} }
} }
} catch (Exception e) { } catch (Exception e) {
SystemTray.logger.error("Error removing menu entry from list.", e); SystemTray.logger.error("Error removing entry from menu.", e);
} }
} }
});
}
/** /**
* This removes a menu entry or sub-menu (via the text label) from the dropdown menu. * This removes a menu entry or sub-menu (via the text label) from the dropdown menu.

View File

@ -104,7 +104,7 @@ class SystemTray extends Menu {
* <p> * <p>
* This is an advanced feature, and it is recommended to leave at 0. * This is an advanced feature, and it is recommended to leave at 0.
*/ */
public static int FORCE_TRAY_TYPE = 3; public static int FORCE_TRAY_TYPE = 0;
@Property @Property
/** /**
@ -875,6 +875,14 @@ class SystemTray extends Menu {
void removePrivate() { void removePrivate() {
} }
/**
* Removes the system tray. This is the same as calling `shutdown()`
*/
public
void remove() {
shutdown();
}
/** /**
* Gets the menu entry for a specified text * Gets the menu entry for a specified text
@ -1048,7 +1056,6 @@ class SystemTray extends Menu {
systemTrayMenu.remove(menuEntry); systemTrayMenu.remove(menuEntry);
} }
/** /**
* This removes a menu entry (via the text label) from the dropdown menu. * This removes a menu entry (via the text label) from the dropdown menu.
* *

View File

@ -64,9 +64,19 @@ class GtkEntry implements MenuEntry {
abstract abstract
void renderText(final String text); void renderText(final String text);
/**
* must always be called in the GTK thread
*/
abstract abstract
void setImage_(final File imageFile); void setImage_(final File imageFile);
/**
* must always be called in the GTK thread
* called when this item is removed. Necessary to cleanup/remove itself
*/
abstract
void removePrivate();
/** /**
* Enables, or disables the sub-menu entry. * Enables, or disables the sub-menu entry.
*/ */
@ -155,11 +165,14 @@ class GtkEntry implements MenuEntry {
} }
} }
/** // a child will always remove itself from the parent.
* will always be on the dispatch thread @Override
*/
public final public final
void remove() { void remove() {
parent.dispatchAndWait(new Runnable() {
@Override
public
void run() {
Gtk.gtk_container_remove(parent._native, _native); Gtk.gtk_container_remove(parent._native, _native);
Gtk.gtk_menu_shell_deactivate(parent._native, _native); Gtk.gtk_menu_shell_deactivate(parent._native, _native);
@ -171,10 +184,8 @@ class GtkEntry implements MenuEntry {
parent.deleteMenu(); parent.deleteMenu();
parent.createMenu(); parent.createMenu();
} }
});
// called when this item is removed. Necessary to cleanup/remove itself }
abstract
void removePrivate();
@Override @Override
public final public final

View File

@ -20,6 +20,7 @@ import static dorkbox.systemTray.SystemTray.TIMEOUT;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -44,14 +45,16 @@ class GtkMenu extends Menu implements MenuEntry {
private boolean obliterateInProgress = false; private boolean obliterateInProgress = false;
// called on dispatch // called on dispatch
GtkMenu(final SystemTray systemTray, final GtkMenu parent, final GtkEntryItem menuEntry) { GtkMenu(final SystemTray systemTray, final GtkMenu parent) {
super(systemTray, parent); super(systemTray, parent);
if (menuEntry != null) { if (parent != null) {
this.menuEntry = new GtkEntryItem(parent, null);
// by default, no callback on a menu entry means it's DISABLED. we have to undo that, because we don't have a callback for menus // by default, no callback on a menu entry means it's DISABLED. we have to undo that, because we don't have a callback for menus
Gtk.gtk_widget_set_sensitive(menuEntry._native, Gtk.TRUE); menuEntry.setEnabled(true);
} else {
this.menuEntry = null;
} }
this.menuEntry = menuEntry;
} }
/** /**
@ -197,25 +200,18 @@ class GtkMenu extends Menu implements MenuEntry {
return menuEntry.hasImage(); return menuEntry.hasImage();
} }
// NO OP.
@Override @Override
public public
void setCallback(final SystemTrayMenuAction callback) { void setCallback(final SystemTrayMenuAction callback) {
// NO OP.
} }
/** /**
* Called when this sub-menu is removed from it's parent menu * Called inside the gdk_threads block
*/ */
protected protected
void removePrivate() { void onMenuAdded(final Pointer menu) {
// delete all of the children // only needed for AppIndicator
obliterateMenu();
// remove the gtk entry item from our parent menu
if (menuEntry != null) {
menuEntry.remove();
}
} }
// some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator. // some GTK libraries DO NOT let us add items AFTER the menu has been attached to the indicator.
@ -224,7 +220,11 @@ class GtkMenu extends Menu implements MenuEntry {
* Deletes the menu, and unreferences everything in it. ALSO recreates ONLY the menu object. * Deletes the menu, and unreferences everything in it. ALSO recreates ONLY the menu object.
*/ */
void deleteMenu() { void deleteMenu() {
if (_native != null && !obliterateInProgress) { if (obliterateInProgress) {
return;
}
if (_native != null) {
// have to remove all other menu entries // have to remove all other menu entries
synchronized (menuEntries) { synchronized (menuEntries) {
for (int i = 0; i < menuEntries.size(); i++) { for (int i = 0; i < menuEntries.size(); i++) {
@ -313,15 +313,11 @@ class GtkMenu extends Menu implements MenuEntry {
} }
/** /**
* Called inside the gdk_threads block * must be called on the dispatch thread
*/ *
void onMenuAdded(final Pointer menu) {
// only needed for AppIndicator
}
/**
* Completely obliterates the menu, no possible way to reconstruct it. * Completely obliterates the menu, no possible way to reconstruct it.
*/ */
private
void obliterateMenu() { void obliterateMenu() {
if (_native != null && !obliterateInProgress) { if (_native != null && !obliterateInProgress) {
obliterateInProgress = true; obliterateInProgress = true;
@ -330,13 +326,7 @@ class GtkMenu extends Menu implements MenuEntry {
synchronized (menuEntries) { synchronized (menuEntries) {
for (int i = 0; i < menuEntries.size(); i++) { for (int i = 0; i < menuEntries.size(); i++) {
MenuEntry menuEntry__ = menuEntries.get(i); MenuEntry menuEntry__ = menuEntries.get(i);
menuEntry__.remove();
if (menuEntry__ instanceof GtkEntry) {
((GtkEntry) menuEntry__).removePrivate();
}
else if (menuEntry__ instanceof GtkMenu) {
((GtkMenu) menuEntry__).removePrivate();
}
} }
menuEntries.clear(); menuEntries.clear();
@ -418,7 +408,7 @@ class GtkMenu extends Menu implements MenuEntry {
// To work around this issue, we destroy then recreate the menu every time something is changed. // To work around this issue, we destroy then recreate the menu every time something is changed.
deleteMenu(); deleteMenu();
GtkMenu subMenu = new GtkMenu(getSystemTray(), GtkMenu.this, new GtkEntryItem(GtkMenu.this, null)); GtkMenu subMenu = new GtkMenu(getSystemTray(), GtkMenu.this);
subMenu.setText(menuText); subMenu.setText(menuText);
subMenu.setImage(imagePath); subMenu.setImage(imagePath);
@ -440,20 +430,42 @@ class GtkMenu extends Menu implements MenuEntry {
return value.get(); return value.get();
} }
// a child will always remove itself from the parent.
@Override @Override
public public
void remove() { void remove() {
dispatchAndWait(new Runnable() {
@Override
public
void run() {
GtkMenu parent = (GtkMenu) getParent(); GtkMenu parent = (GtkMenu) getParent();
Gtk.gtk_container_remove(parent._native, _native); // have to remove from the parent.menuEntries first
Gtk.gtk_menu_shell_deactivate(parent._native, _native); for (Iterator<MenuEntry> iterator = parent.menuEntries.iterator(); iterator.hasNext(); ) {
final MenuEntry entry = iterator.next();
if (entry == GtkMenu.this) {
iterator.remove();
break;
}
}
removePrivate(); // cleans up the menu
parent.remove__(null);
Gtk.gtk_widget_destroy(_native); // delete all of the children of this submenu (must happen before the menuEntry is removed)
obliterateMenu();
// remove the gtk entry item from our parent menu NATIVE components
// NOTE: this will rebuild the parent menu
if (menuEntry != null) {
menuEntry.remove();
} else {
// have to rebuild the menu now... // have to rebuild the menu now...
parent.deleteMenu(); parent.deleteMenu();
parent.createMenu(); parent.createMenu();
} }
}
});
}
} }

View File

@ -27,7 +27,7 @@ abstract
class GtkTypeSystemTray extends GtkMenu { class GtkTypeSystemTray extends GtkMenu {
GtkTypeSystemTray(final SystemTray systemTray) { GtkTypeSystemTray(final SystemTray systemTray) {
super(systemTray, null, null); super(systemTray, null);
} }
public public

View File

@ -72,7 +72,7 @@ class SwingMenu extends Menu implements MenuEntry {
if (OS.isLinux()) { if (OS.isLinux()) {
_native = new SwingSystemTrayLinuxMenuPopup(); _native = new SwingSystemTrayLinuxMenuPopup();
} else { } else {
_native = new SwingSystemTrayMenuPopup(); _native = new SwingSystemTrayMenuWindowsPopup();
} }
} }
} }
@ -321,8 +321,8 @@ class SwingMenu extends Menu implements MenuEntry {
public public
void run() { void run() {
_native.setVisible(false); _native.setVisible(false);
if (_native instanceof SwingSystemTrayMenuPopup) { if (_native instanceof SwingSystemTrayMenuWindowsPopup) {
((SwingSystemTrayMenuPopup) _native).close(); ((SwingSystemTrayMenuWindowsPopup) _native).close();
} }
else if (_native instanceof SwingSystemTrayLinuxMenuPopup) { else if (_native instanceof SwingSystemTrayLinuxMenuPopup) {
((SwingSystemTrayLinuxMenuPopup) _native).close(); ((SwingSystemTrayLinuxMenuPopup) _native).close();
@ -335,12 +335,4 @@ class SwingMenu extends Menu implements MenuEntry {
} }
}); });
} }
/*
* Called when this menu is removed from it's parent menu
*/
protected
void removePrivate() {
remove();
}
} }

View File

@ -28,13 +28,13 @@ import javax.swing.border.EmptyBorder;
* work, so we must implement an "auto-hide" feature that checks if our mouse is still inside a menu every POPUP_HIDE_DELAY seconds * work, so we must implement an "auto-hide" feature that checks if our mouse is still inside a menu every POPUP_HIDE_DELAY seconds
*/ */
public public
class SwingSystemTrayMenuPopup extends JPopupMenu { class SwingSystemTrayMenuWindowsPopup extends JPopupMenu {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
// NOTE: we can use the "hidden dialog" focus window trick... only on windows and mac // NOTE: we can use the "hidden dialog" focus window trick... only on windows and mac
private JDialog hiddenDialog; private JDialog hiddenDialog;
SwingSystemTrayMenuPopup() { SwingSystemTrayMenuWindowsPopup() {
super(); super();
setFocusable(true); setFocusable(true);
// setBorder(new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0)); // borderUI resource border type will get changed! // setBorder(new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0)); // borderUI resource border type will get changed!
@ -54,7 +54,7 @@ class SwingSystemTrayMenuPopup extends JPopupMenu {
this.hiddenDialog.addWindowFocusListener(new WindowFocusListener() { this.hiddenDialog.addWindowFocusListener(new WindowFocusListener() {
@Override @Override
public void windowLostFocus (WindowEvent we ) { public void windowLostFocus (WindowEvent we ) {
SwingSystemTrayMenuPopup.this.setVisible(false); SwingSystemTrayMenuWindowsPopup.this.setVisible(false);
} }
@Override @Override
public void windowGainedFocus (WindowEvent we) {} public void windowGainedFocus (WindowEvent we) {}