用Vala在事件处理程序中重新创建Gtk菜单?

时间:2018-12-16 20:00:48

标签: gtk vala

这是gnome - Gnome3 AppIndicator Vala app cannot show submenu (it auto-closes immediately)? - Ask Ubuntu的延续。我已经意识到,如果子菜单创建代码在activate.connect处理程序中运行,则创建的菜单项将永远不会显示。

但是,在菜单项的activate.connect处理程序中可能有合法使用(子)菜单的运行重新创建的情况,例如,当您需要计算要在子菜单项上显示的文本时。因此,我尝试过修改该示例,该示例在下面以test.vala的形式发布,以为您每次单击“打开”菜单项时都会得到10个带有新文本的子项。在该代码中,“打开”项及其子菜单成为类的属性,并尝试使用Idle.add来运行该子菜单计算,从而避开了activate.connect的上下文-这些都不起作用,创建的菜单仍然消失:

out.gif

我已经在代码中添加了一些打印输出,当我运行它时,我得到:

$ ./test
main() ... 
Main(): ok
Creating MainWindow
class_item_open.activate.connect running, class_submenu (nil) vis -1
activate class_item_open: printChildren for object 0x55c460bb61a0 vis 0
     : child 0x55c460bb71f0 vis 1
activate class_submenu: printChildren for object (nil) vis -1
Idle.add called
createSubmenu called
createSubmenu done, class_submenu 0x55c460ba8580 0
inidle class_item_open: printChildren for object 0x55c460bb61a0 vis 1
     : child 0x55c460bb71f0 vis 1
inidle class_submenu: printChildren for object 0x55c460ba8580 vis 0
     : child 0x55c460bb6350 vis 1
     : child 0x55c460bb6500 vis 1
     : child 0x55c460bb66b0 vis 1
     : child 0x55c460bb6860 vis 1
     : child 0x55c460bb6a10 vis 1
     : child 0x55c460bb6bc0 vis 1
     : child 0x55c460bb6d70 vis 1
     : child 0x55c460bb6f20 vis 1
     : child 0x55c460bb8200 vis 1
     : child 0x55c460bb83b0 vis 1
Idle.add out

# here I click on the "Open" item - and I get this:

class_item_open.activate.connect running, class_submenu 0x55c460ba8580 vis 0
activate class_item_open: printChildren for object 0x55c460bb61a0 vis 1
     : child 0x55c460bb71f0 vis 1
activate class_submenu: printChildren for object 0x55c460ba8580 vis 0
     : child 0x55c460bb6350 vis 1
     : child 0x55c460bb6500 vis 1
     : child 0x55c460bb66b0 vis 1
     : child 0x55c460bb6860 vis 1
     : child 0x55c460bb6a10 vis 1
     : child 0x55c460bb6bc0 vis 1
     : child 0x55c460bb6d70 vis 1
     : child 0x55c460bb6f20 vis 1
     : child 0x55c460bb8200 vis 1
     : child 0x55c460bb83b0 vis 1
Idle.add called
createSubmenu called
createSubmenu done, class_submenu 0x55c460ba8860 0
inidle class_item_open: printChildren for object 0x55c460bb61a0 vis 1
     : child 0x55c460bb71f0 vis 1
inidle class_submenu: printChildren for object 0x55c460ba8860 vis 0
     : child 0x55c460bb6d70 vis 1
     : child 0x55c460bb6bc0 vis 1
     : child 0x55c460bb6a10 vis 1
     : child 0x55c460bb8560 vis 1
     : child 0x55c460bb83b0 vis 1
     : child 0x55c460bb8200 vis 1
     : child 0x55c460bb6f20 vis 1
     : child 0x55c460bb6860 vis 1
     : child 0x55c460bb66b0 vis 1
     : child 0x55c460bb6500 vis 1
Idle.add out

因此,class_submenu最终以.visible为假,即使其所有子项都为true;您可以更改.visible的值,但不会使菜单显示。请注意,指向子项的指针在其创建之间以及按钮单击处理程序运行时(在其重新创建之前)都不会更改,因此引用仍然存在,因此,看起来这与所创建的菜单项无关被垃圾回收作为内存。

所以,我有点困惑-要显示在事件处理程序中创建的子项,我该怎么办?

请注意,如果按照// COMMENT THIS// UNCOMMENT THIS的代码行进行操作,则菜单创建功能仅在启动时运行一次(并且不在activate.connect处理程序中),然后运行所有项目都会显示(尽管显然,在这种情况下,每次打开子菜单时它们的文本都不会更改)。

这里是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
// see also: https://valadoc.org/gtk+-3.0/Gtk.Menu.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 class_submenu;
  public Gtk.MenuItem class_item_open;
  public int tcount;

  public void createSubmenu(){ // note, this being static causes "error: This access invalid outside of instance methods"
    //~ MyIndicator inthis = this;
    stdout.printf("createSubmenu called\n");
    this.class_submenu = new Gtk.Menu();
    this.class_submenu.reserve_toggle_size = true;
    int i;
    for (i = 0; i < 10; i++) {
      this.tcount += 1;
      #if NEWMETHOD
        var subitem = new Gtk.MenuItem.with_label ( "Exit %d".printf(this.tcount) );
      #else
        var subitem = new Gtk.ImageMenuItem.with_label ( "Exit %d".printf(this.tcount) );
      #endif

      subitem.set_reserve_indicator(true);
      this.class_submenu.append(subitem);
      //~ subitem.queue_draw();
      //~ subitem.show();
      subitem.activate.connect(() => {
        App.exit_app();
        exit(0);
      });
      //subitem.activate(); // no way, causes immediate exit!
    }

    //~ this.class_submenu.show_all();
    //~ this.class_item_open.set_submenu(this.class_submenu); //still class_submenu.visible=0 at this point
    //this.class_submenu.set_visible(true); // does change class_submenu.visible to true, but no change (menu items still disappear) with this alone
    //this.class_submenu.queue_draw(); // nope
    //this.class_item_open.show_all(); //nope
    //this.class_item_open.show_all(); //nope
    Idle.add ( () => {
      this.class_item_open.set_submenu(this.class_submenu);
      this.class_submenu.show_all(); // seems not enough
      this.class_item_open.show_all();
      return false;
    });
    stdout.printf("createSubmenu done, class_submenu %p %d\n", class_submenu, (int)class_submenu.visible); stdout.flush();
  }

  public void printChildren(string label, Gtk.Container? container) {
    stdout.printf("%s: printChildren for object %p vis %d\n", label, container, ((void*)container!=null)?(int)container.visible:-1); stdout.flush();
    if ((void*)container!=null) {
      List<weak Gtk.Widget> children = container.get_children();
      foreach(var child in children) {
        stdout.printf("     : child %p vis %d\n", child, (int)child.visible); stdout.flush();
      }
    }
  }

  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();
    this.tcount = 0;

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

    this.class_item_open.set_reserve_indicator(false);

    this.class_item_open.activate.connect(() => {
      stdout.printf("class_item_open.activate.connect running, class_submenu %p vis %d\n", this.class_submenu, ((void*)this.class_submenu!=null)?(int)this.class_submenu.visible:-1); stdout.flush();
      printChildren("activate class_item_open", this.class_item_open);
      printChildren("activate class_submenu", this.class_submenu);
      Idle.add ( () => {
        stdout.printf("Idle.add called\n");
        createSubmenu(); // COMMENT THIS
        while (Gtk.events_pending ())
          Gtk.main_iteration ();
        printChildren("inidle class_item_open", this.class_item_open);
        printChildren("inidle class_submenu", this.class_submenu);
        if ((void*)this.class_submenu!=null) { this.class_submenu.show_all(); }
        stdout.printf("Idle.add out\n");
        return false;
      });
      if ((void*)this.class_submenu!=null) { this.class_submenu.show_all(); }
    });
    //~ createSubmenu(); // UNCOMMENT THIS
    this.class_item_open.activate();

    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 个答案:

没有答案