Consider the source code, pasted below as test.vala
. It is a simple application which should show an icon on top bar/panel, when the icon is clicked it should show a menu with one item in it (Open), when you click on Open it should show a submenu with several items in it. I compile this on:
$ cat /etc/issue
Ubuntu 18.04.1 LTS \n \l
$ uname -a
Linux MyPC 4.15.0-38-generic #41-Ubuntu SMP Wed Oct 10 10:59:38 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
$ printf 'Desktop: %s\nSession: %s\n' "$XDG_CURRENT_DESKTOP" "$GDMSESSION"
Desktop: ubuntu:GNOME
Session: ubuntu
$ gnome-shell --version
GNOME Shell 3.28.3
... and I compile with:
valac -X -D'GETTEXT_PACKAGE="my-indicator"' -D NEWMETHOD --pkg=gtk+-3.0 --pkg appindicator3-0.1 test.vala
... and for this you'll also need to install the libappindicator-dev package.
Then I run the application:
$ ./test
main() ...
Main(): ok
Creating MainWindow
^C
... and the result I get is shown on this animated gif:
Note that appindicator icon is shown (as expected), upon clicking it the first-level menu with the "Open" item is shown (as expected) - but when I click on "Open", I don't really get the submenu that I expect; instead it looks like there is an attempt to open the submenu, and it closes immediately?
What do I need to do, so that this app opens the submenu properly?
Here is test.vala
:
// build with:
// valac -X -D'GETTEXT_PACKAGE="my-indicator"' --pkg=gtk+-3.0 --pkg appindicator3-0.1 test.vala
// "It's not possible to define a preprocessor symbol inside the Vala code (like with C). The only way to define a symbol is to feed it through the valac option -D."
// valac -X -D'GETTEXT_PACKAGE="my-indicator"' -D NEWMETHOD --pkg=gtk+-3.0 --pkg appindicator3-0.1 test.vala
// see also: https://valadoc.org/gtk+-3.0/Gtk.MenuItem.html
using GLib;
using Gtk;
using AppIndicator;
public Main App;
public const string AppName = "Test";
extern void exit(int exit_code);
public class MyIndicator: GLib.Object{
protected Indicator indicator;
protected string icon;
protected string name;
public MyIndicator(){
App.my_indicator = this;
this.name = "My Indicator";
this.icon = "account-logged-in"; // looks like a checkmark
this.indicator = new Indicator("my_indicator", icon, IndicatorCategory.APPLICATION_STATUS);
indicator.set_status(IndicatorStatus.ACTIVE);
var menu = new Gtk.Menu();
// open -------------------------------------
#if NEWMETHOD
var item = new Gtk.MenuItem.with_label(_("Open"));
#else
var item = new Gtk.ImageMenuItem.with_label(_("Open"));
#endif
menu.append(item);
var item_open = item;
item.set_reserve_indicator(false);
item.activate.connect(() => {
var submenu = new Gtk.Menu();
submenu.reserve_toggle_size = true;
//var dummy_window = new Gtk.Window();
//Gtk.Image icon = null;
int i;
for (i = 0; i < 10; i++) {
#if NEWMETHOD
var subitem = new Gtk.MenuItem.with_label ( "Exit %d".printf(i) );
#else
var subitem = new Gtk.ImageMenuItem.with_label ( "Exit %d".printf(i) );
#endif
subitem.set_reserve_indicator(true);
submenu.append(subitem);
subitem.activate.connect(() => {
App.exit_app();
exit(0);
});
//subitem.activate();
}
submenu.show_all();
item_open.set_submenu(submenu);
});
item.activate(); // so it shows submenu triangle
indicator.set_menu(menu);
menu.show_all();
}
}
public class Main : GLib.Object{
public MyIndicator my_indicator;
public static int main (string[] args) {
stdout.printf("main() ... \n");
stdout.flush();
Gtk.init(ref args);
App = new Main(args);
bool success = App.start_application(args);
App.exit_app();
return (success) ? 0 : 1;
}
public Main(string[] args){
stdout.printf("Main(): ok\n");
stdout.flush();
}
public bool start_application(string[] args){
stdout.printf("Creating MainWindow\n");
stdout.flush();
new MyIndicator(); // var ind = new MyIndicator();
//start event loop
Gtk.main();
return true;
}
public void exit_app (){
stdout.printf("exit_app()\n");
stdout.flush();
Gtk.main_quit ();
}
}
EDIT: see also https://stackoverflow.com/questions/53805975/re-creating-gtk-menu-in-event-handler-with-vala
Ok, got it - rewrote the above code so the submenu creation is a separate function, so it got easier to identify that the problem was here:
As the comment says, having the
set_submenu
run in.connect,
causes immediate shutdown of the created submenu. I guess that is because inside the.connect
handler, we have some "anonymous context" or whatever, which causes all local variables created there to be destroyed when the handler exits, regardless of if any of those are referenced elsewhere or not. So the solution is to run thecreateSubmenu
outside of the connect handler.Note that, even with this working code, when compiling it I get:
... but I guess it is not that big of a problem. Probably libappindicator3 needs to be changed.
Anyways, here is the full updated (and working) code of
test.vala
: