I am porting program from Linux Ubuntu 16.04 to Ubuntu 18.04 and 19. I am using appindicator-1.0 in conjunction with Gtk+2.0 menu. Menu consist of constant items and many dynamically added items. If the screen pixel resolution is not so good as it must be then the last menu items are not visible. Ubuntu 16.04 automatically add top and bottom bars with "^" and "v" arrows to scroll menu when mouse pointer is over these bars. But in Linux Ubuntu 18.04 and 19 such scrolling bars are not appearing! Where have I to dig to resolve this issue?
Seems the problem is in Gtk or Gnome settings... But Gtk settings in dconf-editor looks like the same into Ubuntu 16.04 and 18.04. /etc/gtk+-2.0 are the same too. ~/.gtkrc-2.0 is absent. On the other hand menu scrolling is working fine anywhere into usual GTK window with menu bar. Ubuntu 16.04 and Ubuntu 18.04 use the same 12.10.1 libappindicator version. Does it mean that appindicator is guilty?
$ 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;
}
I expect the same menu behavior in Linux Ubuntu 16.04 and 18.04.
New Info:
Seems, it is bug. I have made report about it to bugs.launchpad.net and gitlab.gnome.org.
As the workaround I have connected to signal APP_INDICATOR_SIGNAL_SCROLL_EVENT of the appindicator and realized scrolling as part of my code:
#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;
}
When mouse cursor is placed over appindicator's menu icon then scrolling wheel can be used to scroll configuration menu items to up or down by cycle. On the one hand it is not standard behavior and client cannot guess it with help of his expierence. On the other hand there are scrolled only dynamic menu items. The items order is saved between menu shows. Static (constant) items on the top of the menu are not moved up or down. This can be very convinient in some cases.