Application Panel Indicators

Summary

For the Lucid cycle, the DX team is focusing on a part of the desktop that has become a problem-area:

{attachment:disaster.png}

The current notification area is more and more difficult for users to interact with. Each application behaves differently, they're not accessible, and they're even sometime styled differently.

We propose to migrate the different applets to using simple menus. This change should provide a more consistent interface and be an important step to improve the user experience with the right hand side of the panel.

To implement that change, we're proposing to use a set of dbus APIs. These protocols are based on current technologies, some already used in Ubuntu, others also discussed by members of the FreeDesktop.org group. This includes an implementation already validated by the KDE project.

We are providing a set of libraries compatible with the 2 major desktop environments, along with tools and documentation to help porting applications to use the new protocols.

This effort is managed as part of the Ayatana project group on Launchpad, joining other cross-desktop projects that were delivered during the last Ubuntu development cycles. See resources below.

Notification area usage analysis

The panel indicator area was introduced in an early version of Gnome, on the model of the Microsoft '95 design, as an area dedicated to notifications. It is often called the systray or the notification area.

Use cases

Generally it allows users to very quickly access important application features. For application developers, there are thus certain occasions were it's interesting to host some of the user interface directly on the panel:

Issues with the current situation

Over time the notification area grew organically and somewhat inconsistently, for example, by including various applets which, according to the Guidelines, do not properly belong in the panel indicators area.

{attachment:panel-suck.png}

1. The areas of the panel are not clearly defined or identifiable at a glance because items are no longer organised visually, behaviorally and logically. Each item seems to have an independent purpose and, when clicked, behaves very differently one from the other. We have buttons, menu-ish panel, menu-ish slider and menus. This makes it almost impossible for users to gain a clear overall picture of the function of that area.

2. Users expect items close to each other to behave similarly. They rely on the proximity of applets/applications/menus to predict their behaviours and explain their functions (Usability principles: 1) proximity compatibility (Barnett & Wickens, 1988; Wickens & Carswell, 1995) and 2) consistency (Universal Principle of Design; Nielsen Norman; Bastien & Scapin). The unpredictability of behaviours of items in the panel indicator generates some confusion since users are not always in a position to interpret what the system is doing in response to their actions.

Recommendations: Decisions have to be made about the role of the panel indicators area and items have to be grouped and organized with consistency as to goal, function and behavior in order to offer users a consistent, predictable and therefore intuitive experience.

Proposed changes

We propose to define 2 main areas on the panel:

Additionaly, we propose to turn all of the elements on the right side of the panel into regular menus.

{attachment:panel-suck-less.png}

This way, users will not have to worry about whether a function is accessible with a left or right click, or be suprised when a click on a panel icon suddenly hides or shows a full application window. Additionally, that will help provide:

Note: for compatibility reasons, we'll maintain a notification area zone for a while, to let application developers gradually migrate to the new protocol. Eventually, the old notification area should disappear and applications being able to inject generic/common functions in the system area, while the application-specific features will stay in the "application indicator" zone.

Software Architecture

During the Jaunty and Karmic cycles, we initiated a process to connect the panel and parts of the system using dbus. Traditionally the panel communicates with the rest of the system through a variety of technologies like: Orbit, X properties or Xembed. That flexibility has allowed a lot of applications to use the panel, but the general result is that this starts to show its age.

Using dbus for the panel. We've started with some menus (that we call indicators). We want to continue with all systray/notification-area applications. We call these application-indicators.

There are different parts composing the architecture required to implement the proposed solution.

  1. A panel applet to host the variety of application icons: the applet should display the icons or menu titles, render the content of menus/sub-menus and dialog with the application once the user activates a menu entry.

  2. A library to let applications register a status icon in the panel area; the library should be similar to the existing API, namely gtk_status_icon_new on a Gnome desktop.

  3. A "signaling protocol" between the panel and applications, to maintain that bi-directional link between applications and their icons and menus.

  4. A library to export the context menus, their content and structure, between an application and the panel applet. Trying hard to make that as transparent as possible.

  5. A corresponding protocol to transport the menus and allow for rapid content-related signals to be propagated between the application and the applet, across a dbus link.

We want to do this in a cross-desktop friendly manner that uses existing standards and is easy to migrate to. Therefore we propose this new method of application panel indicators.

In the implementation we developed for the Gnome desktop, we're reusing some existing technologies and introducing a new library.

Note: the extensions to KDE's KStatusNotifier API have been discussed with upstream developers in order to have cross-desktop compatible protocols. This way a KDE application running on a Gnome desktop will have its icon and context menu properly rendered in the Gnome panel. Similarly, a Gnome application running under KDE, should see its icon and menu rendered in the new systray plasmoïd.

Technical Resources

Design Guidelines

Currently applications set how they behave in the notification area. Some use it as a fancy minimize function, others shouldn't have icons in the first place, etc. The design team have put together a design document on how your application should behave.

Libraries

Get the code here or (browse]):

bzr branch lp:indicator-application

We are providing sample implementations in C, C#, and Python using three applications: Rhythmbox, Tomboy, and jockey.

As of 2010-01-14, libappindicator now provides automatic fallback support in the absence of the application indicator area, ie you don't have to check for the presence of the applet in your code, the library will do that for you automatically. If the area is not present on the user desktop, the library will automatically rewrite "app indicator" calls to use the old gtk_status_icon / systray API. Also, libappindicator now provides transparent menu updates. This means that there's no need to do anything when changing a menu content elsewhere in an application: the library connects to gtk signals to trigger dbusmenu updates on the fly and refresh the icons and menus on the panel.

Automake fu

libappindicator is not yet a blessed dependency for Gnome (and won't be for the time being). So here is how third-party application developers can conditionally build for Ubuntu and use the new app. indicators.

WARNING: When using the Mono bindings for libappindicator, you have to check for 'appindicator-sharp-0.1' rather than 'appindicator-0.1'!

1. Test for the existence of libappindicator on the build system

APPINDICATOR_REQUIRED=0.0.7

AC_ARG_ENABLE(appindicator,
                        AS_HELP_STRING([--enable-appindicator[=@<:@no/auto/yes@:>@]],[Build support for application indicators ]),
                        [enable_appindicator=$enableval],
                        [enable_appindicator="auto"])

if test x$enable_appindicator = xauto ; then
        PKG_CHECK_EXISTS([appindicator-0.1 >= $APPINDICATOR_REQUIRED],
                         enable_appindicator="yes",
                         enable_appindicator="no")
fi

if test x$enable_appindicator = xyes ; then
        PKG_CHECK_EXISTS([appindicator-0.1 >= $APPINDICATOR_REQUIRED],,
                         AC_MSG_ERROR([appindicator-0.1 is not installed]))
        PKG_CHECK_MODULES(APP_INDICATOR,
                        appindicator-0.1 >= $APPINDICATOR_REQUIRED)
        AC_SUBST(APP_INDICATOR_CFLAGS)
        AC_SUBST(APP_INDICATOR_LIBS)
        AC_DEFINE(HAVE_APP_INDICATOR, 1, [Have AppIndicator])
fi
AM_CONDITIONAL(HAVE_APP_INDICATOR, test x"$enable_appindicator" = xyes)

2. Enjoy libappindicator!

Public Package Archive(s)

We have set up a] with the core libraries needed for you to install and test libappindicator for Ubuntu Karmic. For Lucid users the libraries will be included in the distro by default. Adding this PPA, upgrading, and running rhythmbox will show you how it works.

If you are interested in porting and getting applications tested, we have set up an] as a service to upstreams who want to get their applications tested. If you need help with this please contact JorgeCastro.

Bug reports, lists and IRC

Priority Applications

Below is a list of applications that need porting to the new API.

List] - Applications should also have a corresponding upstream bug report (Feel free to file and link them). When your patch is ready for review please submit it to the upstream bug tracker attached to the corresponding bug.

Name

Current behavior

Description of neccessary change

Blueprint?/Bug Report?

Blocking Bugs?

gnome-display-properties

Can provide a tray icon if the option is set in the main dialog. It provides a list of radio items and some (cairo drawn?) headers. The menu is the same for left and right click.

Just port the menu over (don't know if the headers can be ported over without an addition of functionality in libdbusmenu.

None

None

Porting Guide for Applications

The application indicators API is similar to the GtkStatusIcon API in some ways, but simpler] in its design.

Indicators are grouped together by category, so it's important for the application to accurately specify its category. Possible categories for indicators include:

The category is set when the indicator is created and isn't changed.

Application indicators fall into one of three states:

The indicator status can be set using app_indicator_set_status().

The icons need to use icon names from themes, direct paths to icon files are not supported. For example, icon names used with gtk_status_icon_new_from_file() won't work.

Typical usage (C version)

Indicators should typically be created using the helper function app_indicator_new(). This returns a pointer to a newly created AppIndicator object, which is may be unref'd normally when it is no longer needed. This function expects three parameters:

  AppIndicator* app_indicator_new (const gchar          *id,
                                   const gchar          *icon_name,
                                   AppIndicatorCategory  category);

The id parameter should be unique to your application. Because app indicators are cross-desktop, the icon_name parameter expects an icon name according to the usual icon naming spec. The category parameter is the indicator category as described above.

Once an indicator object is created, the application may decide to set an attention icon using app_indicator_set_attention_icon().

Lastly, the indicator should be given a GtkMenu object. This can be either created manually or using some other method such as GtkUIManager. Below is an example using GtkUIManager.

   1 #include <gtk/gtk.h>
   2 #include <libappindicator/app-indicator.h>
   3 
   4 static void activate_action (GtkAction *action);
   5 
   6 static GtkActionEntry entries[] = {
   7   { "FileMenu", NULL, "_File" },
   8   { "New",      "document-new", "_New", "<control>N",
   9     "Create a new file", G_CALLBACK (activate_action) },
  10   { "Open",     "document-open", "_Open", "<control>O",
  11     "Open a file", G_CALLBACK (activate_action) },
  12   { "Save",     "document-save", "_Save", "<control>S",
  13     "Save file", G_CALLBACK (activate_action) },
  14   { "Quit",     "application-exit", "_Quit", "<control>Q",
  15     "Exit the application", G_CALLBACK (gtk_main_quit) },
  16 };
  17 static guint n_entries = G_N_ELEMENTS (entries);
  18 
  19 static const gchar *ui_info =
  20 "<ui>"
  21 "  <menubar name='MenuBar'>"
  22 "    <menu action='FileMenu'>"
  23 "      <menuitem action='New'/>"
  24 "      <menuitem action='Open'/>"
  25 "      <menuitem action='Save'/>"
  26 "      <separator/>"
  27 "      <menuitem action='Quit'/>"
  28 "    </menu>"
  29 "  </menubar>"
  30 "  <popup name='IndicatorPopup'>"
  31 "    <menuitem action='New' />"
  32 "    <menuitem action='Open' />"
  33 "    <menuitem action='Save' />"
  34 "    <menuitem action='Quit' />"
  35 "  </popup>"
  36 "</ui>";
  37 
  38 static void
  39 activate_action (GtkAction *action)
  40 {
  41         const gchar *name = gtk_action_get_name (action);
  42         GtkWidget *dialog;
  43 
  44         dialog = gtk_message_dialog_new (NULL,
  45                                          GTK_DIALOG_DESTROY_WITH_PARENT,
  46                                          GTK_MESSAGE_INFO,
  47                                          GTK_BUTTONS_CLOSE,
  48                                          "You activated action: \"%s\"",
  49                                          name);
  50 
  51         g_signal_connect (dialog, "response",
  52                           G_CALLBACK (gtk_widget_destroy), NULL);
  53 
  54         gtk_widget_show (dialog);
  55 }
  56 
  57 int main (int argc, char **argv)
  58 {
  59   GtkWidget *window;
  60   GtkWidget *menubar;
  61   GtkWidget *table;
  62   GtkWidget *sw;
  63   GtkWidget *contents;
  64   GtkWidget *statusbar;
  65   GtkWidget *indicator_menu;
  66   GtkActionGroup *action_group;
  67   GtkUIManager *uim;
  68   AppIndicator *indicator;
  69   GError *error = NULL;
  70 
  71   gtk_init (&argc, &argv);
  72 
  73   /* main window */
  74   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  75   gtk_window_set_title (GTK_WINDOW (window), "Indicator Demo");
  76   gtk_window_set_icon_name (GTK_WINDOW (window), "indicator-messages-new");
  77   g_signal_connect (G_OBJECT (window),
  78                     "destroy",
  79                     G_CALLBACK (gtk_main_quit),
  80                     NULL);
  81 
  82   table = gtk_table_new (1, 5, FALSE);
  83   gtk_container_add (GTK_CONTAINER (window), table);
  84 
  85   /* Menus */
  86   action_group = gtk_action_group_new ("AppActions");
  87   gtk_action_group_add_actions (action_group,
  88                                 entries, n_entries,
  89                                 window);
  90 
  91   uim = gtk_ui_manager_new ();
  92   g_object_set_data_full (G_OBJECT (window),
  93                           "ui-manager", uim,
  94                           g_object_unref);
  95   gtk_ui_manager_insert_action_group (uim, action_group, 0);
  96   gtk_window_add_accel_group (GTK_WINDOW (window),
  97                               gtk_ui_manager_get_accel_group (uim));
  98 
  99   if (!gtk_ui_manager_add_ui_from_string (uim, ui_info, -1, &error))
 100     {
 101       g_message ("Failed to build menus: %s\n", error->message);
 102       g_error_free (error);
 103       error = NULL;
 104     }
 105 
 106   menubar = gtk_ui_manager_get_widget (uim, "/ui/MenuBar");
 107   gtk_widget_show (menubar);
 108   gtk_table_attach (GTK_TABLE (table),
 109                     menubar,
 110                     0, 1,                    0, 1,
 111                     GTK_EXPAND | GTK_FILL,   0,
 112                     0,                       0);
 113 
 114   /* Document */
 115   sw = gtk_scrolled_window_new (NULL, NULL);
 116 
 117   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
 118                                   GTK_POLICY_AUTOMATIC,
 119                                   GTK_POLICY_AUTOMATIC);
 120 
 121   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
 122                                        GTK_SHADOW_IN);
 123 
 124   gtk_table_attach (GTK_TABLE (table),
 125                     sw,
 126                     /* X direction */       /* Y direction */
 127                     0, 1,                   3, 4,
 128                     GTK_EXPAND | GTK_FILL,  GTK_EXPAND | GTK_FILL,
 129                     0,                      0);
 130 
 131   gtk_window_set_default_size (GTK_WINDOW (window),
 132                                200, 200);
 133 
 134   contents = gtk_text_view_new ();
 135   gtk_widget_grab_focus (contents);
 136 
 137   gtk_container_add (GTK_CONTAINER (sw),
 138                      contents);
 139 
 140 
 141   /* Create statusbar */
 142   statusbar = gtk_statusbar_new ();
 143   gtk_table_attach (GTK_TABLE (table),
 144                     statusbar,
 145                     /* X direction */       /* Y direction */
 146                     0, 1,                   4, 5,
 147                     GTK_EXPAND | GTK_FILL,  0,
 148                     0,                      0);
 149 
 150   /* Show the window */
 151   gtk_widget_show_all (window);
 152 
 153   /* Indicator */
 154   indicator = app_indicator_new ("example-simple-client",
 155                                  "indicator-messages",
 156                                  APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
 157 
 158   indicator_menu = gtk_ui_manager_get_widget (uim, "/ui/IndicatorPopup");
 159 
 160   app_indicator_set_status (indicator, APP_INDICATOR_STATUS_ACTIVE);
 161   app_indicator_set_attention_icon (indicator, "indicator-messages-new");
 162 
 163   app_indicator_set_menu (indicator, GTK_MENU (indicator_menu));
 164 
 165   gtk_main ();
 166 
 167   return 0;
 168 }

Python version

PyGI

   1 #!/usr/bin/env python
   2 #
   3 # Copyright 2009-2012 Canonical Ltd.
   4 #
   5 # Authors: Neil Jagdish Patel <neil.patel@canonical.com>
   6 #          Jono Bacon <jono@ubuntu.com>
   7 #          David Planella <david.planella@ubuntu.com>
   8 #
   9 # This program is free software: you can redistribute it and/or modify it 
  10 # under the terms of either or both of the following licenses:
  11 #
  12 # 1) the GNU Lesser General Public License version 3, as published by the 
  13 # Free Software Foundation; and/or
  14 # 2) the GNU Lesser General Public License version 2.1, as published by 
  15 # the Free Software Foundation.
  16 #
  17 # This program is distributed in the hope that it will be useful, but 
  18 # WITHOUT ANY WARRANTY; without even the implied warranties of 
  19 # MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 
  20 # PURPOSE.  See the applicable version of the GNU Lesser General Public 
  21 # License for more details.
  22 #
  23 # You should have received a copy of both the GNU Lesser General Public 
  24 # License version 3 and version 2.1 along with this program.  If not, see 
  25 # <http://www.gnu.org/licenses/>
  26 #
  27 
  28 from gi.repository import Gtk
  29 from gi.repository import AppIndicator3 as appindicator
  30 
  31 
  32 def menuitem_response(w, buf):
  33   print buf
  34 
  35 if __name__ == "__main__":
  36   ind = appindicator.Indicator.new (
  37                         "example-simple-client",
  38                         "indicator-messages",
  39                         appindicator.IndicatorCategory.APPLICATION_STATUS)
  40   ind.set_status (appindicator.IndicatorStatus.ACTIVE)
  41   ind.set_attention_icon ("indicator-messages-new")
  42 
  43   # create a menu
  44   menu = Gtk.Menu()
  45 
  46   # create some 
  47   for i in range(3):
  48     buf = "Test-undermenu - %d" % i
  49 
  50     menu_items = Gtk.MenuItem(buf)
  51 
  52     menu.append(menu_items)
  53 
  54     # this is where you would connect your menu item up with a function:
  55     
  56     # menu_items.connect("activate", menuitem_response, buf)
  57 
  58     # show the items
  59     menu_items.show()
  60 
  61   ind.set_menu(menu)
  62 
  63   Gtk.main()

PyGTK

   1 #!/usr/bin/env python
   2 #
   3 # Copyright 2009 Canonical Ltd.
   4 #
   5 # Authors: Neil Jagdish Patel <neil.patel@canonical.com>
   6 #          Jono Bacon <jono@ubuntu.com>
   7 #
   8 # This program is free software: you can redistribute it and/or modify it 
   9 # under the terms of either or both of the following licenses:
  10 #
  11 # 1) the GNU Lesser General Public License version 3, as published by the 
  12 # Free Software Foundation; and/or
  13 # 2) the GNU Lesser General Public License version 2.1, as published by 
  14 # the Free Software Foundation.
  15 #
  16 # This program is distributed in the hope that it will be useful, but 
  17 # WITHOUT ANY WARRANTY; without even the implied warranties of 
  18 # MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR 
  19 # PURPOSE.  See the applicable version of the GNU Lesser General Public 
  20 # License for more details.
  21 #
  22 # You should have received a copy of both the GNU Lesser General Public 
  23 # License version 3 and version 2.1 along with this program.  If not, see 
  24 # <http://www.gnu.org/licenses/>
  25 #
  26 
  27 import gobject
  28 import gtk
  29 import appindicator
  30 
  31 
  32 def menuitem_response(w, buf):
  33   print buf
  34 
  35 if __name__ == "__main__":
  36   ind = appindicator.Indicator ("example-simple-client",
  37                               "indicator-messages",
  38                               appindicator.CATEGORY_APPLICATION_STATUS)
  39   ind.set_status (appindicator.STATUS_ACTIVE)
  40   ind.set_attention_icon ("indicator-messages-new")
  41 
  42   # create a menu
  43   menu = gtk.Menu()
  44 
  45   # create some 
  46   for i in range(3):
  47     buf = "Test-undermenu - %d" % i
  48 
  49     menu_items = gtk.MenuItem(buf)
  50 
  51     menu.append(menu_items)
  52 
  53     # this is where you would connect your menu item up with a function:
  54     
  55     # menu_items.connect("activate", menuitem_response, buf)
  56 
  57     # show the items
  58     menu_items.show()
  59 
  60   ind.set_menu(menu)
  61 
  62   gtk.main()

C# Example

   1 using Gtk;
   2 using AppIndicator;
   3 
   4 public class IndicatorExample
   5 {
   6         public static void Main ()
   7         {
   8                 Application.Init ();
   9 
  10                 Window win = new Window ("Test");
  11                 win.Resize (200, 200);
  12 
  13                 Label label = new Label ();
  14                 label.Text = "Hello, world!";
  15 
  16                 win.Add (label);
  17 
  18                 ApplicationIndicator indicator = new ApplicationIndicator ("my-id",
  19                                                                            "my-name",
  20                                                                            Category.ApplicationStatus);
  21 
  22                 indicator.Status = Status.Attention;
  23 
  24                 /*
  25                 Menu menu = new Menu ();
  26                 menu.Append (new MenuItem ("Foo"));
  27                 menu.Append (new MenuItem ("Bar"));
  28 
  29                 indicator.Menu = menu;
  30                 */
  31 
  32                 win.ShowAll ();
  33 
  34                 Application.Run ();
  35         }
  36 }

Vala Example

   1 /*
   2  * Copyright 2011-2013 Canonical Ltd.
   3  *
   4  * This program is free software: you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 3, as published
   6  * by the Free Software Foundation.
   7  *
   8  * This program is distributed in the hope that it will be useful, but
   9  * WITHOUT ANY WARRANTY; without even the implied warranties of
  10  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
  11  * PURPOSE.  See the GNU General Public License for more details.
  12  *
  13  * You should have received a copy of the GNU General Public License along
  14  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  15  *
  16  * Compile with:
  17  *  valac --pkg appindicator3-0.1 --pkg gtk+-3.0 app-indicator.vala
  18  *
  19  * Authors:
  20  *   Marco Trevisan (Treviño) <mail@3v1n0.net>
  21  */
  22 
  23 using AppIndicator;
  24 
  25 public class IndicatorExample {
  26         public static int main(string[] args) {
  27                 Gtk.init(ref args);
  28 
  29                 var win = new Gtk.Window();
  30                 win.title = "Indicator Test";
  31                 win.resize(200, 200);
  32                 win.destroy.connect(Gtk.main_quit);
  33 
  34                 var label = new Gtk.Label("Hello, world!");
  35                 win.add(label);
  36 
  37                 var indicator = new Indicator(win.title, "indicator-messages",
  38                                               IndicatorCategory.APPLICATION_STATUS);
  39 
  40                 indicator.set_status(IndicatorStatus.ACTIVE);
  41                 indicator.set_attention_icon("indicator-messages-new");
  42 
  43                 var menu = new Gtk.Menu();
  44 
  45                 var item = new Gtk.MenuItem.with_label("Foo");
  46                 item.activate.connect(() => {
  47                         indicator.set_status(IndicatorStatus.ATTENTION);
  48                 });
  49                 item.show();
  50                 menu.append(item);
  51 
  52                 item = new Gtk.MenuItem.with_label("Bar");
  53                 item.show();
  54                 item.activate.connect(() => {
  55                         indicator.set_status(IndicatorStatus.ATTENTION);
  56                 });
  57                 menu.append(item);
  58 
  59                 indicator.set_menu(menu);
  60 
  61                 win.show_all();
  62 
  63                 Gtk.main();
  64                 return 0;
  65         }
  66 }

Haskell example

   1 -- Either install the Haskell bindings through cabal (cabal install happindicator) or clone the git repository
   2 -- at https://github.com/A1kmm/happindicator and install from there.
   3 
   4 import Graphics.UI.AppIndicator
   5 import Graphics.UI.Gtk
   6 import Control.Monad
   7 
   8 main = do
   9   initGUI
  10   appInd <- appIndicatorNew "appIndicatorDemo" "appointment-soon" AppIndicatorCategoryApplicationStatus
  11   appIndicatorSetStatus appInd AppIndicatorStatusActive
  12   set appInd [ appIndicatorAttentionIconName := Just "folder", 
  13                appIndicatorIconDesc := Just "Demo - non-attention",
  14                appIndicatorAttentionIconDesc := Just "Demo - attention", 
  15                appIndicatorLabel := Just "1" ]
  16   indMenu <- menuNew
  17   forM_ ["Hello", "World", "I'm a MenuItem"] $ \itemName -> do
  18     item <- menuItemNewWithLabel itemName
  19     menuShellAppend indMenu item
  20     item `on` menuItemActivate $ do
  21       appIndicatorSetStatus appInd AppIndicatorStatusAttention
  22     widgetShow item
  23   widgetShow indMenu
  24   appIndicatorSetMenu appInd indMenu
  25   mainGUI

Custom Fallbacks

By default the Application Indicator supports a fallback which will create a GtkStatusIcon with the same icon as the Application Indicator. It supports the menu and responds to all the same state and icon changes. For most applications this should be complete sufficient for their needs. Some applications may wish to handle the callback slightly differently than this. In that case the fallback functions are provided in the class structure so that they can be subclassed and overridden.

There are two functions fallback and unfallback. In general, the fallback function should setup the fallback scenario and the unfallback will take it down. Unfallback will be called if the application indicator returns or at shutdown if that never occurs.

For a very simple example of a fallback function you can look at the [http://bazaar.launchpad.net/~indicator-applet-developers/indicator-application/trunk/annotate/head:/tests/test-libappindicator-fallback-item.c] that creates an object overriding the fallback and unfallback functions. Also, the code in the [http://bazaar.launchpad.net/~indicator-applet-developers/indicator-application/trunk/annotate/head:/src/libappindicator/app-indicator.c] for the fallback and unfallback functions was designed so that it only uses Public API functions so it can be copied directly and modified.

You can also use the 'connection_changed' signal to watch changes to the connections.

Treatment of the API

Menus should be presented from lowest Position value on the leading end, to highest Position value on the trailing end. If an indicator does not declare a Position value, it should fill the 32-bit integer with the most significant byte being filled by the category: 0 for APP_INDICATOR_CATEGORY_APPLICATION_STATUS, 40 for APP_INDICATOR_CATEGORY_COMMUNICATIONS, 80 for APP_INDICATOR_CATEGORY_SYSTEM_SERVICES, 120 for APP_INDICATOR_CATEGORY_HARDWARE, or 160 for APP_INDICATOR_CATEGORY_OTHER, with the remaining three bytes filled by the first 3 bytes of the application name.

The title of the menu should consist of the icon represented by IconName, followed by the text in Label if any. If that text is wider than 6 em, it should be cropped at the trailing end.

Ayatana Indicators Wiki: AyatanaIndicatorApplication (last edited 2021-02-09 14:03:03 by localhost)