Fixed issues with checkboxes not working for AWT/GTK

This commit is contained in:
nathan 2016-12-23 12:18:14 +01:00
parent 4fe1ebf14f
commit 726a1034b0
5 changed files with 92 additions and 65 deletions

View File

@ -444,7 +444,13 @@ class Gtk {
Gtk.isDispatch.set(true);
try {
callback.actionPerformed(new ActionEvent(menuEntry, ActionEvent.ACTION_PERFORMED, ""));
if (menuEntry != null) {
callback.actionPerformed(new ActionEvent(menuEntry, ActionEvent.ACTION_PERFORMED, ""));
} else {
// checkbox entries will not pass the menuEntry in, because they redispatch the click event so that the checkbox state is
// toggled
callback.actionPerformed(null);
}
} finally {
Gtk.isDispatch.set(false);
}

View File

@ -29,7 +29,9 @@ class AwtMenuItemCheckbox implements CheckboxPeer {
private final AwtMenu parent;
private final java.awt.CheckboxMenuItem _native = new java.awt.CheckboxMenuItem();
private volatile ActionListener swingCallback;
// these have to be volatile, because they can be changed from any thread
private volatile ActionListener callback;
private volatile boolean isChecked = false;
// this is ALWAYS called on the EDT.
AwtMenuItemCheckbox(final AwtMenu parent) {
@ -64,15 +66,20 @@ class AwtMenuItemCheckbox implements CheckboxPeer {
@Override
public
void setCallback(final Checkbox menuItem) {
if (swingCallback != null) {
_native.removeActionListener(swingCallback);
if (callback != null) {
_native.removeActionListener(callback);
}
if (menuItem.getCallback() != null) {
swingCallback = new ActionListener() {
callback = menuItem.getCallback(); // can be set to null
if (callback != null) {
callback = new ActionListener() {
@Override
public
void actionPerformed(ActionEvent e) {
// this will run on the EDT, since we are calling it from the EDT
menuItem.setChecked(!isChecked);
// we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
ActionListener cb = menuItem.getCallback();
if (cb != null) {
@ -85,10 +92,7 @@ class AwtMenuItemCheckbox implements CheckboxPeer {
}
};
_native.addActionListener(swingCallback);
}
else {
swingCallback = null;
_native.addActionListener(callback);
}
}
@ -110,12 +114,14 @@ class AwtMenuItemCheckbox implements CheckboxPeer {
@Override
public
void setChecked(final Checkbox checkbox) {
void setChecked(final Checkbox menuItem) {
this.isChecked = menuItem.getChecked();
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
_native.setState(checkbox.getChecked());
_native.setState(isChecked);
}
});
}
@ -131,9 +137,9 @@ class AwtMenuItemCheckbox implements CheckboxPeer {
_native.deleteShortcut();
_native.setEnabled(false);
if (swingCallback != null) {
_native.removeActionListener(swingCallback);
swingCallback = null;
if (callback != null) {
_native.removeActionListener(callback);
callback = null;
}
parent._native.remove(_native);

View File

@ -26,8 +26,8 @@ import dorkbox.systemTray.util.ImageUtils;
abstract
class GtkBaseMenuItem implements EntryPeer {
private static File transparentIcon = null;
// these are necessary BECAUSE GTK menus look funky as hell when there are some menu entries WITH icons and some WITHOUT
private static File transparentIcon = null;
private volatile boolean hasLegitImage = true;
// these have to be volatile, because they can be changed from any thread

View File

@ -15,8 +15,8 @@
*/
package dorkbox.systemTray.nativeUI;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
@ -27,18 +27,16 @@ import dorkbox.systemTray.jna.linux.GCallback;
import dorkbox.systemTray.jna.linux.Gobject;
import dorkbox.systemTray.jna.linux.Gtk;
import dorkbox.systemTray.peer.CheckboxPeer;
import dorkbox.systemTray.util.ImageUtils;
class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCallback {
private static File transparentIcon = null;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final NativeLong nativeLong;
private final GtkMenu parent;
// these have to be volatile, because they can be changed from any thread
private volatile Checkbox checkbox;
private volatile ActionListener callback;
private volatile boolean isChecked = false;
private volatile Pointer image;
// The mnemonic will ONLY show-up once a menu entry is selected. IT WILL NOT show up before then!
@ -54,11 +52,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
super(Gtk.gtk_check_menu_item_new_with_mnemonic(""));
this.parent = parent;
// cannot be done in a static initializer, because the tray icon size might not yet have been determined
if (transparentIcon == null) {
transparentIcon = ImageUtils.getTransparentImage(ImageUtils.ENTRY_SIZE);
}
nativeLong = Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
}
@ -66,15 +59,9 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
int callback(final Pointer instance, final Pointer data) {
if (checkbox != null) {
final ActionListener cb = checkbox.getCallback();
if (cb != null) {
try {
Gtk.proxyClick(checkbox, cb);
} catch (Exception e) {
SystemTray.logger.error("Error calling menu entry checkbox {} click event.", checkbox.getText(), e);
}
}
if (callback != null) {
// this will redispatch to our created callback via `setCallback`
Gtk.proxyClick(null, callback);
}
return Gtk.TRUE;
@ -135,30 +122,54 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
});
}
@SuppressWarnings("Duplicates")
@Override
public
void setCallback(final Checkbox checkbox) {
this.checkbox = checkbox;
void setCallback(final Checkbox menuItem) {
callback = menuItem.getCallback(); // can be set to null
if (callback != null) {
callback = new ActionListener() {
@Override
public
void actionPerformed(ActionEvent e) {
// this will run on the EDT, since we are calling it from the EDT
menuItem.setChecked(!isChecked);
// we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
ActionListener cb = menuItem.getCallback();
if (cb != null) {
try {
cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
} catch (Throwable throwable) {
SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
}
}
}
};
}
}
@Override
public
void setChecked(final Checkbox checkbox) {
this.isChecked = checkbox.getChecked();
Gtk.dispatch(new Runnable() {
@Override
public
void run() {
Gtk.gtk_check_menu_item_set_active(_native, checkbox.getChecked());
Gtk.gtk_check_menu_item_set_active(_native, isChecked);
}
});
}
@Override
public
void setShortcut(final Checkbox menuItem) {
this.mnemonicKey = Character.toLowerCase(menuItem.getShortcut());
void setShortcut(final Checkbox checkbox) {
this.mnemonicKey = Character.toLowerCase(checkbox.getShortcut());
setText(menuItem);
setText(checkbox);
}
@SuppressWarnings("Duplicates")
@ -177,7 +188,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
Gtk.gtk_container_remove(_native, image); // will automatically get destroyed if no other references to it
image = null;
}
checkbox = null;
parent.remove(GtkMenuItemCheckbox.this);
}

View File

@ -33,10 +33,10 @@ class SwingMenuItemCheckbox implements CheckboxPeer {
private final SwingMenu parent;
private final JMenuItem _native = new AdjustedJMenuItem();
// these have to be volatile, because they can be changed from any thread
private volatile ActionListener callback;
private volatile boolean isChecked = false;
private volatile ActionListener swingCallback;
private static ImageIcon checkedIcon;
private static ImageIcon uncheckedIcon;
@ -79,33 +79,38 @@ class SwingMenuItemCheckbox implements CheckboxPeer {
});
}
@SuppressWarnings("Duplicates")
@Override
public
void setCallback(final Checkbox menuItem) {
if (swingCallback != null) {
_native.removeActionListener(swingCallback);
if (callback != null) {
_native.removeActionListener(callback);
}
swingCallback = new ActionListener() {
@Override
public
void actionPerformed(ActionEvent e) {
// this will run on the EDT, since we are calling it from the EDT
menuItem.setChecked(!isChecked);
callback = menuItem.getCallback(); // can be set to null
// we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
ActionListener cb = menuItem.getCallback();
if (cb != null) {
try {
cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
} catch (Throwable throwable) {
SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
if (callback != null) {
callback = new ActionListener() {
@Override
public
void actionPerformed(ActionEvent e) {
// this will run on the EDT, since we are calling it from the EDT
menuItem.setChecked(!isChecked);
// we want it to run on the EDT, but with our own action event info (so it is consistent across all platforms)
ActionListener cb = menuItem.getCallback();
if (cb != null) {
try {
cb.actionPerformed(new ActionEvent(menuItem, ActionEvent.ACTION_PERFORMED, ""));
} catch (Throwable throwable) {
SystemTray.logger.error("Error calling menu entry {} click event.", menuItem.getText(), throwable);
}
}
}
}
};
};
_native.addActionListener(swingCallback);
_native.addActionListener(callback);
}
}
@Override
@ -150,9 +155,9 @@ class SwingMenuItemCheckbox implements CheckboxPeer {
@Override
public
void run() {
if (swingCallback != null) {
_native.removeActionListener(swingCallback);
swingCallback = null;
if (callback != null) {
_native.removeActionListener(callback);
callback = null;
}
parent._native.remove(_native);