我正在将程序从 Linux Ubuntu 16.04 移植到 Ubuntu 18.04 和 19。我将 appindicator-1.0 与 Gtk+2.0 菜单结合使用。菜单由常量项和许多动态添加的项组成。如果屏幕像素分辨率不如它必须的那么好,那么最后的菜单项是不可见的。当鼠标指针悬停在这些条上时,Ubuntu 16.04 会自动添加带有“^”和“v”箭头的顶部和底部条以滚动菜单。但是在 Linux Ubuntu 18.04 和 19 中没有出现这样的滚动条!我在哪里可以挖掘来解决这个问题?
似乎问题出在 Gtk 或 Gnome 设置中......但 dconf-editor 中的 Gtk 设置在 Ubuntu 16.04 和 18.04 中看起来相同。/etc/gtk+-2.0 也一样。~/.gtkrc-2.0 不存在。另一方面,菜单滚动在带有菜单栏的普通 GTK 窗口的任何地方都可以正常工作。Ubuntu 16.04 和 Ubuntu 18.04 使用相同的 12.10.1 libappindicator 版本。这是否意味着appindicator有罪?
$ cat menu.c
#include <gtk/gtk.h>
#include <libappindicator/app-indicator.h>
#define LOGO_PNG "/home/super/my-project/menu-test/logo.png"
AppIndicator* c_indicator;
GtkWidget* c_menu;
void menu_quit_cb(GtkMenuItem* menuitem, gpointer user_data)
{
gtk_main_quit();
}
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
int i;
GtkWidget* item;
char title[128];
c_menu = gtk_menu_new();
item = gtk_menu_item_new_with_label("Quit");
g_signal_connect(item, "activate", G_CALLBACK(menu_quit_cb), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(c_menu), item);
gtk_widget_show(item);
gtk_menu_shell_append(GTK_MENU_SHELL(c_menu), gtk_separator_menu_item_new());
for (i = 1; i <= 100; ++i) {
snprintf(title, sizeof(title), "Item #%03u", i);
item = gtk_menu_item_new_with_label(title);
gtk_menu_shell_append(GTK_MENU_SHELL(c_menu), item);
gtk_widget_show(item);
}
//
c_indicator = app_indicator_new("Menu Test", LOGO_PNG, APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
app_indicator_set_status(c_indicator, APP_INDICATOR_STATUS_ACTIVE);
app_indicator_set_icon(c_indicator, LOGO_PNG);
app_indicator_set_menu(c_indicator, GTK_MENU(c_menu));
gtk_main();
return 0;
}
我希望 Linux Ubuntu 16.04 和 18.04 中的菜单行为相同。
新信息:
看来,是bug。我已经向bugs.launchpad.net和gitlab.gnome.org进行了报告。
作为解决方法,我已连接到 appindicator 的信号 APP_INDICATOR_SIGNAL_SCROLL_EVENT 并在我的代码中实现了滚动:
#include <gtk/gtk.h>
#include <libappindicator/app-indicator.h>
#include <time.h>
#define LOGO_PNG "/home/super/my-project/menu-test/logo.png"
#define AMOUNT_OF_CONSTANT_ITEMS (2)
#define AMOUNT_OF_DYNAMIC_ITEMS (75)
AppIndicator* c_indicator;
GtkWidget* c_menu;
GtkWidget* c_item[AMOUNT_OF_DYNAMIC_ITEMS] = { NULL };
gint c_item_first = 0; // GTK_MENU_ITEM(c_item[c_item_first]) is the first visible dynamic item
static struct timespec time_diff(struct timespec end, struct timespec start)
{
struct timespec temp;
if (end.tv_nsec < start.tv_nsec) {
temp.tv_sec = end.tv_sec - start.tv_sec - 1;
temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
} else {
temp.tv_sec = end.tv_sec - start.tv_sec;
temp.tv_nsec = end.tv_nsec - start.tv_nsec;
}
return temp;
}
static gboolean not_elapsed_enough()
{
static struct timespec last_moment = { 0, 0 };
struct timespec current_moment;
struct timespec temp;
clock_gettime(CLOCK_MONOTONIC_COARSE, ¤t_moment);
if (0 == last_moment.tv_sec && 0 == last_moment.tv_nsec) {
last_moment = current_moment;
return FALSE;
}
temp = time_diff(current_moment, last_moment);
if (temp.tv_sec == 0 && temp.tv_nsec < 99999999)
return TRUE; // do not enough time elapsed
last_moment = current_moment;
return FALSE;
}
void appind_scroll_event_cb(AppIndicator* indicator, gint delta, GdkScrollDirection direction, gpointer user_data)
{
if (not_elapsed_enough()) return;
if (GDK_SCROLL_UP == direction) {
GtkWidget* item = c_item[c_item_first];
g_object_ref(item); // do not destroy item togather with container
gtk_container_remove(GTK_CONTAINER(c_menu), item); // remove item from menu
gtk_menu_shell_insert(GTK_MENU_SHELL(c_menu), item, AMOUNT_OF_CONSTANT_ITEMS + AMOUNT_OF_DYNAMIC_ITEMS);
g_object_unref(item);
gtk_widget_show(item);
++c_item_first;
if (c_item_first >= AMOUNT_OF_DYNAMIC_ITEMS)
c_item_first = 0;
} else if (GDK_SCROLL_DOWN == direction) {
if (0 == c_item_first)
c_item_first = AMOUNT_OF_DYNAMIC_ITEMS;
--c_item_first;
GtkWidget* item = c_item[c_item_first];
g_object_ref(item);
gtk_container_remove(GTK_CONTAINER(c_menu), item);
gtk_menu_shell_insert(GTK_MENU_SHELL(c_menu), item, AMOUNT_OF_CONSTANT_ITEMS);
g_object_unref(item);
gtk_widget_show(item);
}
}
int main(int argc, char *argv[])
{
gtk_init(&argc, &argv);
int i;
GtkWidget* item;
char title[128];
c_menu = gtk_menu_new();
item = gtk_menu_item_new_with_label("Quit");
g_signal_connect(item, "activate", G_CALLBACK(gtk_main_quit), NULL);
gtk_menu_shell_append(GTK_MENU_SHELL(c_menu), item);
gtk_widget_show(item);
item = gtk_menu_item_new_with_label("---------------------");
gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
gtk_menu_shell_append(GTK_MENU_SHELL(c_menu), item);
gtk_widget_show(item);
for (i = 1; i <= AMOUNT_OF_DYNAMIC_ITEMS; ++i) {
snprintf(title, sizeof(title), "Item #%03u", i);
item = gtk_menu_item_new_with_label(title);
gtk_menu_shell_append(GTK_MENU_SHELL(c_menu), item);
gtk_widget_show(item);
c_item[i - 1] = item;
}
//
c_indicator = app_indicator_new("Menu Test", LOGO_PNG, APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
app_indicator_set_status(c_indicator, APP_INDICATOR_STATUS_ACTIVE);
app_indicator_set_icon(c_indicator, LOGO_PNG);
app_indicator_set_menu(c_indicator, GTK_MENU(c_menu));
g_signal_connect(c_indicator, APP_INDICATOR_SIGNAL_SCROLL_EVENT, G_CALLBACK(appind_scroll_event_cb), NULL);
gtk_main();
return 0;
}
当鼠标光标放在 appindicator 的菜单图标上时,滚轮可用于循环向上或向下滚动配置菜单项。一方面,这不是标准行为,客户凭经验无法猜测。另一方面,只有滚动的动态菜单项。项目顺序保存在菜单显示之间。菜单顶部的静态(常量)项不会向上或向下移动。在某些情况下,这可能非常方便。