Пересоздание Gtk Menu в обработчике событий с помощью Vala? - PullRequest
0 голосов
/ 16 декабря 2018

Это своего рода продолжение gnome - приложение Vala Gnome3 AppIndicator не может отображать подменю (оно сразу закрывается автоматически)?- Спросите Ubuntu .Там я понял, что если код создания подменю запускается в обработчике activ.connect, созданные пункты меню никогда не отображаются.

Тем не менее, может быть законное использование запуска пересоздания (под) меню вОбработчик activ.connect для элемента меню, например, когда вам нужно вычислить, какой текст будет отображаться в элементах подменю.Поэтому я попытался изменить этот пример, опубликованный ниже как test.vala, с мыслью, что каждый раз, когда вы нажимаете на элемент меню «Открыть», вы получаете 10 подпунктов с новым текстом.В этом коде элемент «Открыть» и его подменю становятся свойствами класса и пытались использовать Idle.add для выполнения этого вычисления подменю, чтобы избежать контекста activ.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 false, даже если все его дети имеют его true;Вы можете изменить значение .visible, но меню не отобразится.Обратите внимание, что указатели на дочерние элементы не меняются между их созданием, и когда запускается обработчик нажатия кнопки (до их повторного создания), поэтому ссылки все еще там, - таким образом, похоже, что это не относится к созданным пунктам менюбыть мусором, собранным как память.

Итак, я немного озадачен - что мне нужно сделать, чтобы подэлементы, созданные в обработчике событий, отображались?

Обратите внимание, если вы делаете, как сказано в строках с // COMMENT THISи // UNCOMMENT THIS, затем вы запускаете функцию создания меню только один раз при запуске (и из обработчика activ.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 ();
  }
}
...