这是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的上下文-这些都不起作用,创建的菜单仍然消失:
我已经在代码中添加了一些打印输出,当我运行它时,我得到:
$ ./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 ();
}
}