forked from dorkbox/SystemTray
WIP for checkboxes on AppIndicators
This commit is contained in:
parent
e4ff8da145
commit
8dddee95dd
@ -226,7 +226,12 @@ class GtkMenu extends GtkBaseMenuItem implements MenuPeer {
|
|||||||
entry.bind(item, parentMenu, parentMenu.getSystemTray());
|
entry.bind(item, parentMenu, parentMenu.getSystemTray());
|
||||||
}
|
}
|
||||||
else if (entry instanceof Checkbox) {
|
else if (entry instanceof Checkbox) {
|
||||||
GtkMenuItemCheckbox item = new GtkMenuItemCheckbox(GtkMenu.this);
|
// Additionally, we can ask the SystemTray WHAT KIND of tray it is, since it will know by this point in time.
|
||||||
|
// necessary because of bad layout decisions by AppIndicators for checkbox items
|
||||||
|
|
||||||
|
// WIP. The checkbox (if appIndicator) is always black. This could cause problems depending on theme
|
||||||
|
// boolean isAppIndicator = SystemTray.get().getMenu() instanceof _AppIndicatorNativeTray;
|
||||||
|
GtkMenuItemCheckbox item = new GtkMenuItemCheckbox(GtkMenu.this, false);
|
||||||
add(item, index);
|
add(item, index);
|
||||||
((Checkbox) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
((Checkbox) entry).bind(item, parentMenu, parentMenu.getSystemTray());
|
||||||
}
|
}
|
||||||
|
@ -26,14 +26,19 @@ import dorkbox.systemTray.jna.linux.GCallback;
|
|||||||
import dorkbox.systemTray.jna.linux.Gobject;
|
import dorkbox.systemTray.jna.linux.Gobject;
|
||||||
import dorkbox.systemTray.jna.linux.Gtk;
|
import dorkbox.systemTray.jna.linux.Gtk;
|
||||||
import dorkbox.systemTray.peer.CheckboxPeer;
|
import dorkbox.systemTray.peer.CheckboxPeer;
|
||||||
|
import dorkbox.systemTray.util.ImageUtils;
|
||||||
|
|
||||||
// ElementaryOS shows the checkbox on the right, everyone else is on the left. With eOS, we CANNOT show the spacer image. It does not work
|
// ElementaryOS shows the checkbox on the right, everyone else is on the left. With eOS, we CANNOT show the spacer image. It does not work
|
||||||
class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCallback {
|
class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCallback {
|
||||||
|
private static String checkedFile;
|
||||||
|
private static String uncheckedFile;
|
||||||
|
|
||||||
private final GtkMenu parent;
|
private final GtkMenu parent;
|
||||||
|
|
||||||
// these have to be volatile, because they can be changed from any thread
|
// these have to be volatile, because they can be changed from any thread
|
||||||
private volatile ActionListener callback;
|
private volatile ActionListener callback;
|
||||||
private volatile boolean isChecked = false;
|
private volatile boolean isChecked = false;
|
||||||
|
private volatile Pointer checkedImage;
|
||||||
private volatile Pointer image;
|
private volatile Pointer image;
|
||||||
|
|
||||||
// The mnemonic will ONLY show-up once a menu entry is selected. IT WILL NOT show up before then!
|
// The mnemonic will ONLY show-up once a menu entry is selected. IT WILL NOT show up before then!
|
||||||
@ -41,16 +46,35 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
// GtkStatusIconTray will show on mouse+keyboard movement
|
// GtkStatusIconTray will show on mouse+keyboard movement
|
||||||
private volatile char mnemonicKey = 0;
|
private volatile char mnemonicKey = 0;
|
||||||
private final long handlerId;
|
private final long handlerId;
|
||||||
|
private final boolean isAppIndicator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
* called from inside dispatch thread. ONLY creates the menu item, but DOES NOT attach it!
|
||||||
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
* this is a FLOATING reference. See: https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html#floating-ref
|
||||||
|
*
|
||||||
|
* note: AppIndicator tray's DO NOT show the spacer image for checkboxes so they are "shifted left", which looks awkward.
|
||||||
*/
|
*/
|
||||||
GtkMenuItemCheckbox(final GtkMenu parent) {
|
GtkMenuItemCheckbox(final GtkMenu parent, final boolean isAppIndicator) {
|
||||||
super(Gtk.gtk_check_menu_item_new_with_mnemonic(""));
|
super(isAppIndicator ? Gtk.gtk_image_menu_item_new_with_mnemonic("") : Gtk.gtk_check_menu_item_new_with_mnemonic(""));
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
this.isAppIndicator = isAppIndicator;
|
||||||
|
|
||||||
handlerId = Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
|
handlerId = Gobject.g_signal_connect_object(_native, "activate", this, null, 0);
|
||||||
|
|
||||||
|
if (checkedFile == null) {
|
||||||
|
// from Brankic1979, public domain
|
||||||
|
checkedFile = ImageUtils.resizeAndCache(ImageUtils.ENTRY_SIZE, ImageUtils.class.getResource("checked_32.png")).getAbsolutePath();
|
||||||
|
uncheckedFile = ImageUtils.getTransparentImage(ImageUtils.ENTRY_SIZE).getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAppIndicator) {
|
||||||
|
setCheckedIconForAppIndicators();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Gobject.g_signal_handler_block(_native, handlerId);
|
||||||
|
Gtk.gtk_check_menu_item_set_active(_native, false);
|
||||||
|
Gobject.g_signal_handler_unblock(_native, handlerId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// called by native code ONLY
|
// called by native code ONLY
|
||||||
@ -151,7 +175,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setChecked(final Checkbox menuItem) {
|
void setChecked(final Checkbox menuItem) {
|
||||||
boolean checked = menuItem.getChecked();
|
final boolean checked = menuItem.getChecked();
|
||||||
|
|
||||||
// only dispatch if it's actually different
|
// only dispatch if it's actually different
|
||||||
if (checked != this.isChecked) {
|
if (checked != this.isChecked) {
|
||||||
@ -161,6 +185,7 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
|
if (!isAppIndicator) {
|
||||||
// note: this will trigger "activate", which will then trigger the callback.
|
// note: this will trigger "activate", which will then trigger the callback.
|
||||||
// we assume this is consistent across ALL versions and variants of GTK
|
// we assume this is consistent across ALL versions and variants of GTK
|
||||||
// https://github.com/GNOME/gtk/blob/master/gtk/gtkcheckmenuitem.c#L317
|
// https://github.com/GNOME/gtk/blob/master/gtk/gtkcheckmenuitem.c#L317
|
||||||
@ -168,11 +193,36 @@ class GtkMenuItemCheckbox extends GtkBaseMenuItem implements CheckboxPeer, GCall
|
|||||||
Gobject.g_signal_handler_block(_native, handlerId);
|
Gobject.g_signal_handler_block(_native, handlerId);
|
||||||
Gtk.gtk_check_menu_item_set_active(_native, isChecked);
|
Gtk.gtk_check_menu_item_set_active(_native, isChecked);
|
||||||
Gobject.g_signal_handler_unblock(_native, handlerId);
|
Gobject.g_signal_handler_unblock(_native, handlerId);
|
||||||
|
} else {
|
||||||
|
setCheckedIconForAppIndicators();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private
|
||||||
|
void setCheckedIconForAppIndicators() {
|
||||||
|
if (checkedImage != null) {
|
||||||
|
Gtk.gtk_container_remove(_native, checkedImage); // will automatically get destroyed if no other references to it
|
||||||
|
checkedImage = null;
|
||||||
|
Gtk.gtk_widget_show_all(_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.isChecked) {
|
||||||
|
checkedImage = Gtk.gtk_image_new_from_file(checkedFile);
|
||||||
|
} else {
|
||||||
|
checkedImage = Gtk.gtk_image_new_from_file(uncheckedFile);
|
||||||
|
}
|
||||||
|
Gtk.gtk_image_menu_item_set_image(_native, checkedImage);
|
||||||
|
|
||||||
|
// must always re-set always-show after setting the image
|
||||||
|
Gtk.gtk_image_menu_item_set_always_show_image(_native, true);
|
||||||
|
|
||||||
|
Gtk.gtk_widget_show_all(_native);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void setShortcut(final Checkbox checkbox) {
|
void setShortcut(final Checkbox checkbox) {
|
||||||
|
Loading…
Reference in New Issue
Block a user