/**
* SECTION:hildon-app-menu
- * @short_description: Widget representing the application menu in the Hildon framework.
+ * @short_description: Application menu for Hildon applications.
*
- * The #HildonAppMenu is a GTK widget which represents an application
- * menu in the Hildon framework.
+ * #HildonAppMenu is an application menu for applications in the Hildon
+ * framework.
*
* This menu opens from the top of the screen and contains a number of
* entries (#GtkButton) organized in one or two columns, depending on
* if the screen is resized). Entries are added left to right and top
* to bottom.
*
- * Besides that, the #HildonAppMenu can contain a group of filter buttons
- * (#GtkToggleButton or #GtkRadioButton).
+ * Besides that, #HildonAppMenu can contain a group of filter buttons
+ * (#GtkToggleButton or #GtkRadioButton). Filters are meant to change
+ * the way data is presented in the application, rather than change
+ * the layout of the menu itself. For example, a file manager can have
+ * filters to decide the order used to display a list of files (name,
+ * date, size, etc.).
*
* To use a #HildonAppMenu, add it to a #HildonWindow using
* hildon_window_set_app_menu(). The menu will appear when the user
#include "hildon-app-menu-private.h"
#include "hildon-window.h"
#include "hildon-banner.h"
-
-static GdkWindow *
-grab_transfer_window_get (GtkWidget *widget);
+#include "hildon-animation-actor.h"
static void
hildon_app_menu_repack_items (HildonAppMenu *menu,
HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
/* Add the item to the menu */
- gtk_widget_show (GTK_WIDGET (item));
g_object_ref_sink (item);
priv->buttons = g_list_insert (priv->buttons, item, position);
- hildon_app_menu_repack_items (menu, position);
+ if (GTK_WIDGET_VISIBLE (item))
+ hildon_app_menu_repack_items (menu, position);
/* Enable accelerators */
g_signal_connect (item, "can-activate-accel", G_CALLBACK (can_activate_accel), NULL);
HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH);
/* Add the filter to the menu */
- gtk_widget_show (GTK_WIDGET (filter));
g_object_ref_sink (filter);
priv->filters = g_list_append (priv->filters, filter);
- hildon_app_menu_repack_filters (menu);
+ if (GTK_WIDGET_VISIBLE (filter))
+ hildon_app_menu_repack_filters (menu);
/* Enable accelerators */
g_signal_connect (filter, "can-activate-accel", G_CALLBACK (can_activate_accel), NULL);
{
HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (menu);
- hildon_app_menu_repack_items (menu, g_list_index (priv->buttons, item));
+ if (! priv->inhibit_repack)
+ hildon_app_menu_repack_items (menu, g_list_index (priv->buttons, item));
}
static void
GParamSpec *arg1,
HildonAppMenu *menu)
{
- hildon_app_menu_repack_filters (menu);
+ HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (menu);
+
+ if (! priv->inhibit_repack)
+ hildon_app_menu_repack_filters (menu);
}
static void
static void
hildon_app_menu_show_all (GtkWidget *widget)
{
+ HildonAppMenu *menu = HILDON_APP_MENU (widget);
HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (widget);
+ priv->inhibit_repack = TRUE;
+
/* Show children, but not self. */
g_list_foreach (priv->buttons, (GFunc) gtk_widget_show_all, NULL);
g_list_foreach (priv->filters, (GFunc) gtk_widget_show_all, NULL);
+
+ priv->inhibit_repack = FALSE;
+
+ hildon_app_menu_repack_items (menu, 0);
+ hildon_app_menu_repack_filters (menu);
}
static void
hildon_app_menu_hide_all (GtkWidget *widget)
{
+ HildonAppMenu *menu = HILDON_APP_MENU (widget);
HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (widget);
+ priv->inhibit_repack = TRUE;
+
/* Hide children, but not self. */
g_list_foreach (priv->buttons, (GFunc) gtk_widget_hide_all, NULL);
g_list_foreach (priv->filters, (GFunc) gtk_widget_hide_all, NULL);
+
+ priv->inhibit_repack = FALSE;
+
+ hildon_app_menu_repack_items (menu, 0);
+ hildon_app_menu_repack_filters (menu);
}
/*
* Yes, this is a hack. See NB#111027 */
if (HILDON_IS_BANNER (i->data)) {
gtk_widget_hide (i->data);
- } else {
+ } else if (!HILDON_IS_ANIMATION_ACTOR (i->data)) {
intruder_found = TRUE;
}
}
GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->map (widget);
- /* Grab pointer and keyboard */
- if (priv->transfer_window == NULL) {
- gboolean has_grab = FALSE;
-
- priv->transfer_window = grab_transfer_window_get (widget);
-
- if (gdk_pointer_grab (priv->transfer_window, TRUE,
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
- GDK_POINTER_MOTION_MASK, NULL, NULL,
- GDK_CURRENT_TIME) == GDK_GRAB_SUCCESS) {
- if (gdk_keyboard_grab (priv->transfer_window, TRUE,
- GDK_CURRENT_TIME) == GDK_GRAB_SUCCESS) {
- has_grab = TRUE;
- } else {
- gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
- GDK_CURRENT_TIME);
- }
- }
-
- if (has_grab) {
- gtk_grab_add (widget);
- } else {
- gdk_window_destroy (priv->transfer_window);
- priv->transfer_window = NULL;
- }
- }
-
- /* Make the menu temporary when it's mapped, so it's closed if a
- * new window appears */
- gtk_window_set_is_temporary (GTK_WINDOW (widget), TRUE);
-
- priv->find_intruder_idle_id = gdk_threads_add_idle (hildon_app_menu_find_intruder, widget);
-}
-
-static void
-hildon_app_menu_unmap (GtkWidget *widget)
-{
- HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE(widget);
-
- /* Remove the grab */
- if (priv->transfer_window != NULL) {
- gdk_display_pointer_ungrab (gtk_widget_get_display (widget),
- GDK_CURRENT_TIME);
- gtk_grab_remove (widget);
-
- gdk_window_destroy (priv->transfer_window);
- priv->transfer_window = NULL;
- }
-
- GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->unmap (widget);
-
- gtk_window_set_is_temporary (GTK_WINDOW (widget), FALSE);
+ if (priv->find_intruder_idle_id == 0)
+ priv->find_intruder_idle_id = gdk_threads_add_idle (hildon_app_menu_find_intruder, widget);
}
static void
}
static gboolean
-hildon_app_menu_button_press (GtkWidget *widget,
- GdkEventButton *event)
-{
- int x, y;
- HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE(widget);
-
- gdk_window_get_position (widget->window, &x, &y);
-
- /* Whether the button has been pressed outside the widget */
- priv->pressed_outside = (event->x_root < x || event->x_root > x + widget->allocation.width ||
- event->y_root < y || event->y_root > y + widget->allocation.height);
-
- if (GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_press_event) {
- return GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_press_event (widget, event);
- } else {
- return FALSE;
- }
-}
-
-static gboolean
-hildon_app_menu_button_release (GtkWidget *widget,
- GdkEventButton *event)
-{
- HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE(widget);
-
- if (priv->pressed_outside) {
- int x, y;
- gboolean released_outside;
-
- gdk_window_get_position (widget->window, &x, &y);
-
- /* Whether the button has been released outside the widget */
- released_outside = (event->x_root < x || event->x_root > x + widget->allocation.width ||
- event->y_root < y || event->y_root > y + widget->allocation.height);
-
- if (released_outside) {
- gtk_widget_hide (widget);
- }
-
- priv->pressed_outside = FALSE; /* Always reset pressed_outside to FALSE */
- }
-
- if (GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_release_event) {
- return GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_release_event (widget, event);
- } else {
- return FALSE;
- }
-}
-
-static gboolean
hildon_app_menu_delete_event_handler (GtkWidget *widget,
GdkEventAny *event)
{
return TRUE;
}
-/* Grab transfer window (based on the one from GtkMenu) */
-static GdkWindow *
-grab_transfer_window_get (GtkWidget *widget)
-{
- GdkWindow *window;
- GdkWindowAttr attributes;
- gint attributes_mask;
-
- attributes.x = 0;
- attributes.y = 0;
- attributes.width = 10;
- attributes.height = 10;
- attributes.window_type = GDK_WINDOW_TEMP;
- attributes.wclass = GDK_INPUT_ONLY;
- attributes.override_redirect = TRUE;
- attributes.event_mask = 0;
-
- attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
-
- window = gdk_window_new (gtk_widget_get_root_window (widget),
- &attributes, attributes_mask);
- gdk_window_set_user_data (window, widget);
-
- gdk_window_show (window);
-
- return window;
-}
-
static void
hildon_app_menu_size_request (GtkWidget *widget,
GtkRequisition *requisition)
hildon_app_menu_apply_style (GtkWidget *widget)
{
GdkScreen *screen;
+ gint filter_group_width;
guint horizontal_spacing, vertical_spacing, filter_vertical_spacing;
guint inner_border, external_border;
HildonAppMenuPrivate *priv;
gtk_widget_style_get (widget,
"horizontal-spacing", &horizontal_spacing,
"vertical-spacing", &vertical_spacing,
+ "filter-group-width", &filter_group_width,
"filter-vertical-spacing", &filter_vertical_spacing,
"inner-border", &inner_border,
"external-border", &external_border,
/* Set inner border */
gtk_container_set_border_width (GTK_CONTAINER (widget), inner_border);
+ /* Set width of the group of filter buttons */
+ gtk_widget_set_size_request (GTK_WIDGET (priv->filters_hbox), filter_group_width, -1);
+
/* Compute width request */
screen = gtk_widget_get_screen (widget);
if (gdk_screen_get_width (screen) < gdk_screen_get_height (screen)) {
external_border = 0;
}
priv->width_request = gdk_screen_get_width (screen) - external_border * 2;
- gtk_window_move (GTK_WINDOW (widget), external_border, 0);
+
+ if (widget->window)
+ gdk_window_move_resize (widget->window,
+ external_border, 0, 1, 1);
+
gtk_widget_queue_resize (widget);
}
gint start_from)
{
HildonAppMenuPrivate *priv;
- gint row, col;
+ gint row, col, nvisible, i;
GList *iter;
priv = HILDON_APP_MENU_GET_PRIVATE(menu);
- /* Remove buttons from their parent */
- if (start_from != -1) {
- for (iter = g_list_nth (priv->buttons, start_from); iter != NULL; iter = iter->next) {
+ i = nvisible = 0;
+ for (iter = priv->buttons; iter != NULL; iter = iter->next) {
+ /* Count number of visible items */
+ if (GTK_WIDGET_VISIBLE (iter->data))
+ nvisible++;
+ /* Remove buttons from their parent */
+ if (start_from != -1 && i >= start_from) {
GtkWidget *item = GTK_WIDGET (iter->data);
GtkWidget *parent = gtk_widget_get_parent (item);
if (parent) {
gtk_container_remove (GTK_CONTAINER (parent), item);
}
}
+ i++;
+ }
- /* If items have been removed, recalculate the size of the menu */
+ /* If items have been removed, recalculate the size of the menu */
+ if (start_from != -1)
gtk_window_resize (GTK_WINDOW (menu), 1, 1);
- }
+
+ /* Set the final size now to avoid unnecessary resizes later */
+ if (nvisible > 0)
+ gtk_table_resize (priv->table, ((nvisible - 1) / priv->columns) + 1, priv->columns);
/* Add buttons */
row = col = 0;
}
}
}
-
- /* The number of rows/columns might have changed, so we have to
- * resize the table */
- if (col == 0) {
- gtk_table_resize (priv->table, MAX (row, 1), priv->columns);
- } else {
- gtk_table_resize (priv->table, row + 1, priv->columns);
- }
}
/**
priv->parent_window = NULL;
priv->transfer_window = NULL;
priv->pressed_outside = FALSE;
+ priv->inhibit_repack = FALSE;
+ priv->last_pressed_button = NULL;
priv->buttons = NULL;
priv->filters = NULL;
priv->columns = 2;
widget_class->show_all = hildon_app_menu_show_all;
widget_class->hide_all = hildon_app_menu_hide_all;
widget_class->map = hildon_app_menu_map;
- widget_class->unmap = hildon_app_menu_unmap;
widget_class->realize = hildon_app_menu_realize;
widget_class->unrealize = hildon_app_menu_unrealize;
widget_class->grab_notify = hildon_app_menu_grab_notify;
widget_class->key_press_event = hildon_app_menu_key_press;
- widget_class->button_press_event = hildon_app_menu_button_press;
- widget_class->button_release_event = hildon_app_menu_button_release;
widget_class->style_set = hildon_app_menu_style_set;
widget_class->delete_event = hildon_app_menu_delete_event_handler;
widget_class->size_request = hildon_app_menu_size_request;
gtk_widget_class_install_style_property (
widget_class,
+ g_param_spec_int (
+ "filter-group-width",
+ "Width of the group of filter buttons",
+ "Total width of the group of filter buttons, "
+ "or -1 to use the natural size request.",
+ -1, G_MAXINT, 444,
+ G_PARAM_READABLE));
+
+ gtk_widget_class_install_style_property (
+ widget_class,
g_param_spec_uint (
"filter-vertical-spacing",
"Vertical spacing between filters and menu items",