/**
* 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"
+#include "hildon-animation-actor.h"
static GdkWindow *
grab_transfer_window_get (GtkWidget *widget);
GParamSpec *arg1,
HildonAppMenu *menu);
+static gboolean
+menu_item_button_event (GtkButton *item,
+ GdkEventButton *event,
+ GtkWidget *menu);
+
static void
remove_item_from_list (GList **list,
gpointer item);
g_signal_connect_swapped (item, "clicked", G_CALLBACK (gtk_widget_hide), menu);
g_signal_connect (item, "notify::visible", G_CALLBACK (item_visibility_changed), menu);
+ /* Keep track of the latest menu item to receive a button-press event */
+ g_signal_connect (item, "button-press-event", G_CALLBACK (menu_item_button_event), menu);
+ g_signal_connect (item, "button-release-event", G_CALLBACK (menu_item_button_event), menu);
+
/* Remove item from list when it is destroyed */
g_object_weak_ref (G_OBJECT (item), (GWeakNotify) remove_item_from_list, &(priv->buttons));
}
g_signal_connect_swapped (filter, "clicked", G_CALLBACK (gtk_widget_hide), menu);
g_signal_connect (filter, "notify::visible", G_CALLBACK (filter_visibility_changed), menu);
+ /* Keep track of the latest menu item to receive a button-press event */
+ g_signal_connect (filter, "button-press-event", G_CALLBACK (menu_item_button_event), menu);
+ g_signal_connect (filter, "button-release-event", G_CALLBACK (menu_item_button_event), menu);
+
/* Remove filter from list when it is destroyed */
g_object_weak_ref (G_OBJECT (filter), (GWeakNotify) remove_item_from_list, &(priv->filters));
}
hildon_app_menu_repack_filters (menu);
}
+static gboolean
+menu_item_button_event (GtkButton *item,
+ GdkEventButton *event,
+ GtkWidget *menu)
+{
+ HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE (menu);
+
+ if (event->type == GDK_BUTTON_PRESS) {
+ priv->last_pressed_button = item;
+ } else if (event->type == GDK_BUTTON_RELEASE) {
+ /* A pressed button might not receive the button-release event due
+ * to the grab that HildonAppMenu has, so we have to simulate that
+ * event. See NB#108337 */
+ if (priv->last_pressed_button && priv->last_pressed_button != item) {
+ gtk_button_released (priv->last_pressed_button);
+ }
+ priv->last_pressed_button = NULL;
+ }
+ return FALSE;
+}
+
static void
remove_item_from_list (GList **list,
gpointer item)
* 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;
}
}
{
HildonAppMenuPrivate *priv = HILDON_APP_MENU_GET_PRIVATE(widget);
+ if (priv->transfer_window == NULL)
+ priv->transfer_window = grab_transfer_window_get (widget);
+
GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->map (widget);
/* Grab pointer and keyboard */
- if (priv->transfer_window == NULL) {
+ 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 |
* 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);
+ if (priv->find_intruder_idle_id == 0)
+ priv->find_intruder_idle_id = gdk_threads_add_idle (hildon_app_menu_find_intruder, widget);
}
static void
}
priv->pressed_outside = FALSE; /* Always reset pressed_outside to FALSE */
+ } else if (priv->last_pressed_button) {
+ menu_item_button_event (NULL, event, widget);
}
if (GTK_WIDGET_CLASS (hildon_app_menu_parent_class)->button_release_event) {
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)) {
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->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;
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",