Documentation/Maemo 5 Developer Guide/Porting Software/Migration

Because Hildon 2.2 is a big step up from previous versions, many approaches in the development of your program's graphical user interface may need to be migrated to this new interface type.


Contents

[edit] Menus

HildonAppMenu represents a big difference from the normal menus. If your application is to be finger-friendly, a view specific menu, instead of application menu, is mandatory.

The Maemo 4 application menu typically contains most or the full set of functionality of the application, organized in hierarchical menus.

In Maemo 5, there is a strict maximum limit 10 items in a view menu, and there is no menu hierarchy.

The Maemo 5 View menus are not application menus, in the sense that a view menu only contains commands relevant to that particular view. The only exception to this is the Root view menu, which additionally contains menu item(s) for application settings.

Because the view menu also uses buttons, you can easily add them with the callback functions that the original menu items had.

For example, consider an advanced text editing program. The original menu bar had the following menus:

Example 10.1. Menu example

File
    \ New
    \ Open
    \ Save
    \ Save As
    \ Quit
Edit
    \ Copy
    \ Paste
    \ Cut
    \ Indent Type
        \ Tabs
        \ Spaces
    \ Preferences
View
    \ Zoom In
    \ Zoom Out
    \ Normal Size
    \ Highlight
        \ C
        \ Python
        \ JAVA
Help
    \ Contents
    \ About

Guidelines:

  • In general, commands are specific to their view
  • The initial view (root view) is the only access point to application settings
  • Additional menu commands can be made accessible via dialogs opened from the view menu, if those commands cannot be presented directly in the UI or simply removed.

A text editor does not need subviews, so more or less all commands need to fit in a single view. See Redesigning From Maemo 4 to Maemo 5 for details about sub view redesign.

Changes:

  • File menu commands: keep "New", "Open" and "Save". Remove "Save as" and "Quit". The "Save" command can function as "Save as" in cases where the source file is read only or the folder is no longer accessible.
    • NOTE: the document should be automatically saved (and re-opened) whenever the application is closed (or re-opened). There is no need to show any confirmation notes for this process.
  • Edit menu commands: remove "Cut/Copy/Paste", those should be used with keyboard shortcuts. Put all the rest inside "Preferences" dialog and rename it to "Settings"
  • View menu commands: "Zoom" in a text editor is not very useful so remove it (for changing the font size, simply HW +/- keys would be enough). Put "Highlight mode" in to "Settings".
  • Help menu commands: Remove help. It's not part of the Hildon 2.2 UI Style.

The following example shows the implementation of the menu following the above decisions. The first figure below shows the final result of the app menu. The other showd the dialog activated by the menu command "Settings".

Screenshot of a HildonAppMenu
HildonAppMenu example


Screenshot of the settings dialog
Settings dialog example


Example 10.2. Migrating a menu

#include <hildon/hildon.h>
#include <libintl.h>
 
static void
menu_settings_cb (GtkButton *widget, gpointer data)
{
   /* Code to set show settings dialog... */
   g_assert(data);
   GtkDialog *dialog = GTK_DIALOG(
      gtk_dialog_new_with_buttons("Settings",
                                  GTK_WINDOW(data),
                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                  dgettext("hildon-libs", "wdgt_bd_save"),
                                  GTK_RESPONSE_OK,
                                  NULL));
   gint response = 0;
   GtkSizeGroup *titlegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
   GtkSizeGroup *valuegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
   /* Syntax highlight */
   GtkWidget *syntax_selector = hildon_touch_selector_new_text ();
   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (syntax_selector), "C");
   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (syntax_selector), "JAVA");
   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (syntax_selector), "Python");
   GtkWidget *syntax_picker = hildon_picker_button_new (HILDON_SIZE_THUMB_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
                                                        HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
   hildon_button_set_title (HILDON_BUTTON (syntax_picker), "Syntax highlight");
   hildon_picker_button_set_selector (HILDON_PICKER_BUTTON(syntax_picker),
                                      HILDON_TOUCH_SELECTOR(syntax_selector));
   hildon_picker_button_set_active (HILDON_PICKER_BUTTON(syntax_picker), 0);
 
   gtk_button_set_alignment (GTK_BUTTON (syntax_picker), 0.0, 0.5);
   hildon_button_add_size_groups (HILDON_BUTTON (syntax_picker), titlegroup, valuegroup, NULL);
   gtk_container_add (GTK_CONTAINER (dialog->vbox), syntax_picker);
 
   /* Indent type */
   GtkWidget *indent_selector = hildon_touch_selector_new_text ();
   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (indent_selector), "Tabs");
   hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (indent_selector), "Spaces");
   GtkWidget *indent_picker = hildon_picker_button_new (HILDON_SIZE_THUMB_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
                                                        HILDON_BUTTON_ARRANGEMENT_HORIZONTAL);
   hildon_button_set_title (HILDON_BUTTON (indent_picker), "Indent type");
   hildon_picker_button_set_selector (HILDON_PICKER_BUTTON(indent_picker), 
                                      HILDON_TOUCH_SELECTOR(indent_selector));
   hildon_picker_button_set_active (HILDON_PICKER_BUTTON(indent_picker), 0);
 
   gtk_button_set_alignment (GTK_BUTTON (indent_picker), 0.0, 0.5);
   hildon_button_add_size_groups (HILDON_BUTTON (indent_picker), titlegroup, valuegroup, NULL);
   gtk_container_add (GTK_CONTAINER (dialog->vbox), indent_picker);
 
   gtk_widget_show_all (GTK_WIDGET (dialog));
 
   response = gtk_dialog_run(dialog);
   if (response == GTK_RESPONSE_OK) {   
       /* "Save" button has been pressed, save/update the settings here */
 
       /* ... */
   }
 
   /* close the dialog */
   gtk_widget_destroy(GTK_WIDGET(dialog));
}
 
static HildonAppMenu *
build_menu (GtkWidget *window)
{
   HildonSizeType button_size = HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH;
   HildonAppMenu *menu = HILDON_APP_MENU (hildon_app_menu_new ());
   GtkButton *button;
 
   /* Menus like New, Open, etc that would open new dialogs (no submenus)
    * are not assigned any action in this example
    */
 
   button = GTK_BUTTON (hildon_gtk_button_new (button_size));
   gtk_button_set_label (button, "New");
   hildon_app_menu_append (menu, GTK_BUTTON (button));
 
   button = GTK_BUTTON (hildon_gtk_button_new (button_size));
   gtk_button_set_label (button, "Open");
   hildon_app_menu_append (menu, GTK_BUTTON (button));
 
   button = GTK_BUTTON (hildon_gtk_button_new (button_size));
   gtk_button_set_label (button, "Save");
   hildon_app_menu_append (menu, GTK_BUTTON (button));
 
   button = GTK_BUTTON (hildon_gtk_button_new (button_size));
   gtk_button_set_label (button, "Settings");
   g_signal_connect (G_OBJECT (button), "clicked",
                     G_CALLBACK (menu_settings_cb), window);
   hildon_app_menu_append (menu, GTK_BUTTON (button));
 
   gtk_widget_show_all (GTK_WIDGET (menu));
 
   return menu;
}
 
int
main (int argc, char **argv)
{
   hildon_gtk_init (&argc, &argv);
 
   GtkWidget *window;
   window = hildon_stackable_window_new ();
   gtk_window_set_title (GTK_WINDOW (window), "Editor");
 
   HildonAppMenu *menu = build_menu (window);
   hildon_window_set_app_menu (HILDON_WINDOW (window),     menu);
 
   GtkWidget *contents = gtk_label_new ("This example shows how an app menu could be "\
                                        "migrated.\nMenu item callbacks "\
                                        "are not fully implemented.");
   gtk_container_add (GTK_CONTAINER (window), contents);
 
   /* ... other widgets in the application */
 
   g_signal_connect (G_OBJECT (window), "destroy",
                     G_CALLBACK (gtk_main_quit), NULL);
 
   gtk_widget_show_all (window);
 
   gtk_main ();
   return 0;
}

[edit] Choices

Some widgets for choosing options also need to be adapted to the new Hildon way of designing interfaces. If you have several radio buttons to express a choice, replacing them with a picker button is a good idea.

Consider a dialog that includes, among other widgets, a frame labeled "Proxy Preferences" and three radio buttons: "None", "Auto" and "Manual", displayed vertically like Figure 10.1 "Three radio buttons" shows. The following example shows how such task could be accomplished using a picker button. The frame label becomes the picker button's title and the three radio buttons' labels become the choices in the picker's touch selector. The screenshot below shows the resulting graphical user interface.

Screenshot of a picker button dialog
Example of choices in a picker button

Example 10.3. Migrating choice widgets

#include <hildon/hildon.h>
int
main (int argc, char **argv)
{
    hildon_gtk_init (&argc, &argv);
    GtkWidget *window;
    window = hildon_window_new ();
    gtk_window_set_title (GTK_WINDOW (window), "Preferences");
    g_signal_connect (G_OBJECT (window), "destroy",
                      G_CALLBACK (gtk_main_quit), NULL);
    HildonPickerButton *picker = HILDON_PICKER_BUTTON (hildon_picker_button_new (
                            HILDON_SIZE_THUMB_HEIGHT | HILDON_SIZE_HALFSCREEN_WIDTH,
                                                HILDON_BUTTON_ARRANGEMENT_VERTICAL));
    hildon_button_set_title (HILDON_BUTTON (picker), "Proxy preferences:");
    HildonTouchSelector *selector = HILDON_TOUCH_SELECTOR (hildon_touch_selector_new_text ());
    hildon_picker_button_set_selector (picker, selector);
    hildon_touch_selector_append_text (selector, "None");
    hildon_touch_selector_append_text (selector, "Auto");
    hildon_touch_selector_append_text (selector, "Manual");
    hildon_picker_button_set_active (picker, 0);
    GtkVBox *contents = GTK_VBOX (gtk_vbox_new (12, FALSE));
    /* ... other widgets placed here */
    gtk_box_pack_end (GTK_BOX (contents), GTK_WIDGET (picker), FALSE, FALSE, 0);
    gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (contents));
    gtk_widget_show_all (window);
    gtk_main ();
    return 0;
}

[edit] Notebooks

Because the use of notebooks is not advised in Hildon, the way to migrate a Maemo 4 dialog with a notebook to Maemo 5, is to simply put all the items inside a single pannable dialog.