web-dev-qa-db-fra.com

L'application Gnome3 AppIndicator Vala ne peut pas afficher le sous-menu (elle se ferme automatiquement)?

Considérez le code source, collé ci-dessous en tant que test.vala. C’est une application simple qui devrait afficher une icône dans la barre/le panneau du haut. Lorsque l’icône est cliquée, elle devrait afficher un menu avec un élément (Ouvrir). Lorsque vous cliquez sur Ouvrir, elle devrait afficher un sous-menu contenant plusieurs éléments. . Je compile ceci sur:

$ 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

... et je compile avec:

valac -X -D'GETTEXT_PACKAGE="my-indicator"' -D NEWMETHOD --pkg=gtk+-3.0 --pkg appindicator3-0.1 test.vala

... et pour cela, vous aurez également besoin d'installer le paquet libappindicator-dev .

Ensuite, je lance l'application:

$ ./test 
main() ... 
Main(): ok
Creating MainWindow
^C

... et le résultat obtenu est affiché sur ce gif animé:

out.gif

Notez que l'icône de l'indicateur est affichée (comme prévu). Lorsque vous cliquez dessus, le menu de premier niveau avec l'élément "Ouvrir" s'affiche (comme prévu) - mais lorsque je clique sur "Ouvrir", le sous-menu ne s'affiche pas vraiment. Je m'attends à; au lieu de cela, on dirait qu'il y a une tentative d'ouvrir le sous-menu et qu'il se ferme immédiatement?

Que dois-je faire pour que cette application ouvre correctement le sous-menu?

Voici 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 ();
  }
}
2
sdaau

EDIT: voir aussi https://stackoverflow.com/questions/53805975/re-creating-gtk-menu-in-event-handler-with-vala


Ok, compris - réécrit le code ci-dessus pour que la création du sous-menu soit une fonction distincte, ce qui facilite l'identification du problème:

item_open.activate.connect(() => {
  //~ item_open.set_submenu(createSubmenu()); // NO; having the set_submenu run in .connect, causes immediate shutdown of the created submenu!!
  stdout.printf("item.activate.connect running\n");
});
item_open.set_submenu(createSubmenu()); // is OK here
item_open.activate(); // so it shows submenu triangle

Comme le dit le commentaire, l'exécution de set_submenu dans .connect, provoque l'arrêt immédiat du sous-menu créé. Je suppose que cela est dû au fait qu’à l’intérieur du gestionnaire .connect, nous avons un "contexte anonyme" ou quoi que ce soit, ce qui entraîne la destruction de toutes les variables locales créées à la sortie du gestionnaire, qu’il en soit ou non référencé ailleurs. . La solution consiste donc à exécuter le createSubmenu à l'extérieur du gestionnaire de connexion.

Notez que, même avec ce code de travail, lors de sa compilation, je reçois:

$ valac -X -D'GETTEXT_PACKAGE="my-indicator"' -D NEWMETHOD --pkg=gtk+-3.0 --pkg appindicator3-0.1 test.vala
/tmp/test.vala.c: In function ‘my_indicator_createSubmenu’:
/tmp/test.vala.c:182:52: warning: passing argument 2 of ‘gtk_menu_Shell_append’ from incompatible pointer type [-Wincompatible-pointer-types]
    gtk_menu_Shell_append ((GtkMenuShell*) _tmp11_, _tmp12_);
                                                    ^~~~~~~
In file included from /usr/include/gtk-3.0/gtk/gtkmenu.h:33:0,
                 from /usr/include/gtk-3.0/gtk/gtklabel.h:34,
                 from /usr/include/gtk-3.0/gtk/gtkaccellabel.h:35,
                 from /usr/include/gtk-3.0/gtk/gtk.h:33,
                 from /usr/include/libappindicator3-0.1/libappindicator/app-indicator.h:33,
                 from /tmp/test.vala.c:15:
/usr/include/gtk-3.0/gtk/gtkmenushell.h:91:10: note: expected ‘GtkWidget * {aka struct _GtkWidget *}’ but argument is of type ‘GtkMenuItem * {aka struct _GtkMenuItem *}’
 void     gtk_menu_Shell_append         (GtkMenuShell *menu_Shell,
          ^~~~~~~~~~~~~~~~~~~~~
/tmp/test.vala.c: In function ‘my_indicator_construct’:
/tmp/test.vala.c:260:47: warning: passing argument 2 of ‘gtk_menu_Shell_append’ from incompatible pointer type [-Wincompatible-pointer-types]
  gtk_menu_Shell_append ((GtkMenuShell*) menu, item_open);
                                               ^~~~~~~~~
In file included from /usr/include/gtk-3.0/gtk/gtkmenu.h:33:0,
                 from /usr/include/gtk-3.0/gtk/gtklabel.h:34,
                 from /usr/include/gtk-3.0/gtk/gtkaccellabel.h:35,
                 from /usr/include/gtk-3.0/gtk/gtk.h:33,
                 from /usr/include/libappindicator3-0.1/libappindicator/app-indicator.h:33,
                 from /tmp/test.vala.c:15:
/usr/include/gtk-3.0/gtk/gtkmenushell.h:91:10: note: expected ‘GtkWidget * {aka struct _GtkWidget *}’ but argument is of type ‘GtkMenuItem * {aka struct _GtkMenuItem *}’
 void     gtk_menu_Shell_append         (GtkMenuShell *menu_Shell,
          ^~~~~~~~~~~~~~~~~~~~~
/tmp/test.vala.c:265:40: warning: passing argument 2 of ‘gtk_menu_item_set_submenu’ from incompatible pointer type [-Wincompatible-pointer-types]
  gtk_menu_item_set_submenu (item_open, _tmp10_);
                                        ^~~~~~~
In file included from /usr/include/gtk-3.0/gtk/gtkcheckmenuitem.h:33:0,
                 from /usr/include/gtk-3.0/gtk/gtk.h:72,
                 from /usr/include/libappindicator3-0.1/libappindicator/app-indicator.h:33,
                 from /tmp/test.vala.c:15:
/usr/include/gtk-3.0/gtk/gtkmenuitem.h:120:12: note: expected ‘GtkWidget * {aka struct _GtkWidget *}’ but argument is of type ‘GtkMenu * {aka struct _GtkMenu *}’
 void       gtk_menu_item_set_submenu          (GtkMenuItem         *menu_item,
            ^~~~~~~~~~~~~~~~~~~~~~~~~

... mais je suppose que ce n'est pas un gros problème. Probablement libappindicator3 doit être changé.

Quoi qu'il en soit, voici le code complet mis à jour (et fonctionnel) de 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 Gtk.Menu createSubmenu(){
    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(); // no way, causes immediate exit!
    }

    submenu.show_all();
    return submenu;
  }
  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);

    /*
    // from https://valadoc.org/gtk+-3.0/Gtk.Menu.html
        // MenuBar:
        //Gtk.MenuBar bar = new Gtk.MenuBar (); //error: Argument 1: Cannot convert from `unowned Gtk.MenuBar?' to `unowned Gtk.Menu?'
        Gtk.Menu bar = new Gtk.Menu ();
        //indicator.add (bar); // error: The name `add' does not exist in the context of `AppIndicator.Indicator'
    indicator.set_menu(bar);

        // File:
        Gtk.MenuItem item_file = new Gtk.MenuItem.with_label ("File");
        bar.add (item_file);

        Gtk.Menu filemenu = new Gtk.Menu ();
        item_file.set_submenu (filemenu);

        Gtk.MenuItem item_open = new Gtk.MenuItem.with_label ("Open");
        filemenu.add (item_open);

        // Submenu:
        Gtk.Menu submenu = new Gtk.Menu ();
        item_open.set_submenu (submenu);

        Gtk.MenuItem item_foo = new Gtk.MenuItem.with_label ("foo");
        submenu.add (item_foo);

        Gtk.MenuItem item_bar = new Gtk.MenuItem.with_label ("bar");
        submenu.add (item_bar);

    bar.show_all();
    */

    var menu = new Gtk.Menu();

    // open -------------------------------------
    #if NEWMETHOD
      var item_open = new Gtk.MenuItem.with_label(_("Open"));
    #else
      var item_open = new Gtk.ImageMenuItem.with_label(_("Open"));
    #endif
    menu.append(item_open);

    item_open.set_reserve_indicator(false);

    item_open.activate.connect(() => {
      //~ item_open.set_submenu(createSubmenu()); // NO; having the set_submenu run in .connect, causes immediate shutdown of the created submenu!!
      stdout.printf("item.activate.connect running\n");
    });
    item_open.set_submenu(createSubmenu()); // is OK here
    item_open.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 ();
  }
}
0
sdaau