Proper fix for checkbox state activating callbacks in GTK.

This commit is contained in:
nathan 2017-01-21 14:58:49 +01:00
parent ea83c7e006
commit 687035c3f1
2 changed files with 13 additions and 25 deletions

View File

@ -43,6 +43,7 @@ class Gobject {
}
}
// objdump -T /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 | grep block
public static native void g_object_get(Pointer object, String objectName, PointerByReference objectVal, Pointer nullValue);
@ -51,5 +52,9 @@ class Gobject {
public static native void g_object_force_floating(Pointer object);
public static native void g_object_ref_sink(Pointer object);
public static native void g_signal_connect_object(Pointer instance, String detailed_signal, Callback c_handler, Pointer object, int connect_flags);
// note: the return type here MUST be long to avoid issues on freeBSD. NativeLong (previously used) worked on everything except BSD.
public static native long g_signal_connect_object(Pointer instance, String detailed_signal, Callback c_handler, Pointer object, int connect_flags);
public static native void g_signal_handler_block(Pointer instance, long handlerId);
public static native void g_signal_handler_unblock(Pointer instance, long handlerId);
}

View File

@ -17,7 +17,6 @@ package dorkbox.systemTray.nativeUI;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.atomic.AtomicBoolean;
import com.sun.jna.Pointer;
@ -41,6 +40,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
// AppIndicators will only show if you use the keyboard to navigate
// GtkStatusIconTray will show on mouse+keyboard movement
private volatile char mnemonicKey = 0;
private final long handlerId;
/**
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
@ -50,7 +50,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
super(Gtk.gtk_check_menu_item_new_with_mnemonic(""));
this.parent = parent;
Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
handlerId = Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
}
// called by native code ONLY
@ -120,10 +120,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
});
}
AtomicBoolean triggeredSetChecked = new AtomicBoolean(false);
@SuppressWarnings({"Duplicates", "StatementWithEmptyBody"})
@Override
public
@ -135,14 +131,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
void actionPerformed(ActionEvent e) {
if (triggeredSetChecked.getAndSet(false)) {
// note: changing the state of the checkbox will trigger "activate", which will then trigger the callback. We don't want that.
// we assume this is consistent across ALL versions and variants of GTK
// https://github.com/GNOME/gtk/blob/master/gtk/gtkcheckmenuitem.c#L317
return;
}
// this will run on the EDT, since we are calling it from the EDT
// this will run on the EDT, since we are calling it from the EDT. This can ALSO recursively call the callback
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)
@ -156,7 +145,6 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
}
}
};
}
}
@ -173,18 +161,13 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
@Override
public
void run() {
// note: this will trigger "activate", which will then trigger the callback. We don't want to do that, so we hack around it.
// BECAUSE we are on the GTK thread, the "set_active" call is QUEUED... so we check the callback state AFTER this.
// note: this will trigger "activate", which will then trigger the callback.
// we assume this is consistent across ALL versions and variants of GTK
// https://github.com/GNOME/gtk/blob/master/gtk/gtkcheckmenuitem.c#L317
// this disables the signal handler, then enables it
Gobject.g_signal_handler_block(_native, handlerId);
Gtk.gtk_check_menu_item_set_active(_native, isChecked);
if (callback != null) {
// only need to worry about this if we have a callback specified.
// This is because of how GTK handles setting the checkbox state + callbacks
triggeredSetChecked.set(true);
}
Gobject.g_signal_handler_unblock(_native, handlerId);
}
});
}