* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* #GtkMenuItem in a #GtkMenuBar or popped up by activating a
* #GtkMenuItem in another #GtkMenu.
*
- * A #GtkMenu can also be popped up by activating a #GtkOptionMenu.
+ * A #GtkMenu can also be popped up by activating a #GtkComboBox.
* Other composite widgets such as the #GtkNotebook can pop up a
* #GtkMenu as well.
*
* if (event->type == GDK_BUTTON_PRESS)
* {
* event_button = (GdkEventButton *) event;
- * if (event_button->button == 3)
+ * if (event_button->button == GDK_BUTTON_SECONDARY)
* {
* gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
* event_button->button, event_button->time);
#include "gtkaccellabel.h"
#include "gtkaccelmap.h"
+#include "gtkadjustment.h"
#include "gtkbindings.h"
#include "gtkcheckmenuitem.h"
#include "gtkmain.h"
#include "gtkmenuprivate.h"
#include "gtkmenuitemprivate.h"
#include "gtkmenushellprivate.h"
-#include "gtktearoffmenuitem.h"
#include "gtkwindow.h"
-#include "gtkhbox.h"
-#include "gtkvscrollbar.h"
+#include "gtkbox.h"
+#include "gtkscrollbar.h"
#include "gtksettings.h"
#include "gtkprivate.h"
+#include "gtkwidgetpath.h"
+#include "gtkwidgetprivate.h"
+#include "gtkdnd.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
+#include "gtkwidgetprivate.h"
+
+#include "deprecated/gtktearoffmenuitem.h"
+
+
+#include "a11y/gtkmenuaccessible.h"
#define NAVIGATION_REGION_OVERSHOOT 50 /* How much the navigation region
* extends below the submenu
GtkMenuDetachFunc detacher;
};
-struct _OldGtkMenuPrivate
-{
- gint x;
- gint y;
- gboolean initially_pushed_in;
-
- GDestroyNotify position_func_data_destroy;
-
- /* info used for the table */
- guint *heights;
- gint heights_length;
- gint requested_height;
-
- gint monitor_num;
-
- /* Cached layout information */
- gint n_rows;
- gint n_columns;
-
- guint accel_size;
-
- gchar *title;
-
- /* Arrow states */
- GtkStateFlags lower_arrow_state;
- GtkStateFlags upper_arrow_state;
-
- /* navigation region */
- int navigation_x;
- int navigation_y;
- int navigation_width;
- int navigation_height;
-
- guint have_layout : 1;
- guint seen_item_enter : 1;
- guint have_position : 1;
- guint ignore_button_release : 1;
- guint no_toggle_size : 1;
-};
-
struct _GtkMenuPopdownData
{
GtkMenu *menu;
gint offset);
static void gtk_menu_grab_notify (GtkWidget *widget,
gboolean was_grabbed);
+static gboolean gtk_menu_captured_event (GtkWidget *widget,
+ GdkEvent *event);
+
static void gtk_menu_stop_scrolling (GtkMenu *menu);
static void gtk_menu_remove_scroll_timeout (GtkMenu *menu);
static gboolean gtk_menu_scroll_timeout (gpointer data);
-static gboolean gtk_menu_scroll_timeout_initial (gpointer data);
-static void gtk_menu_start_scrolling (GtkMenu *menu);
static void gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
GtkWidget *menu_item);
-1, G_MAXINT, -1,
GTK_PARAM_READWRITE));
- gtk_widget_class_install_style_property (widget_class,
- g_param_spec_int ("vertical-padding",
- P_("Vertical Padding"),
- P_("Extra space at the top and bottom of the menu"),
- 0,
- G_MAXINT,
- 1,
- GTK_PARAM_READABLE));
-
/**
* GtkMenu:reserve-toggle-size:
*
TRUE,
GTK_PARAM_READWRITE));
+ /**
+ * GtkMenu:horizontal-padding:
+ *
+ * Extra space at the left and right edges of the menu.
+ *
+ * Deprecated: 3.8: use the standard padding CSS property (through objects
+ * like #GtkStyleContext and #GtkCssProvider); the value of this style
+ * property is ignored.
+ */
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("horizontal-padding",
P_("Horizontal Padding"),
0,
G_MAXINT,
0,
- GTK_PARAM_READABLE));
+ GTK_PARAM_READABLE |
+ G_PARAM_DEPRECATED));
+
+ /**
+ * GtkMenu:vertical-padding:
+ *
+ * Extra space at the top and bottom of the menu.
+ *
+ * Deprecated: 3.8: use the standard padding CSS property (through objects
+ * like #GtkStyleContext and #GtkCssProvider); the value of this style
+ * property is ignored.
+ */
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_int ("vertical-padding",
+ P_("Vertical Padding"),
+ P_("Extra space at the top and bottom of the menu"),
+ 0,
+ G_MAXINT,
+ 1,
+ GTK_PARAM_READABLE |
+ G_PARAM_DEPRECATED));
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("vertical-offset",
GTK_PARAM_READWRITE));
/**
- * GtkMenu:arrow-scaling
+ * GtkMenu:arrow-scaling:
*
* Arbitrary constant to scale down the size of the scroll arrow.
*
GTK_SCROLL_PAGE_DOWN);
g_type_class_add_private (gobject_class, sizeof (GtkMenuPrivate));
+
+ gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_ACCESSIBLE);
}
case GDK_KEY_RELEASE:
handled = gtk_widget_event (menu, event);
break;
+ case GDK_WINDOW_STATE:
+ /* Window for the menu has been closed by the display server or by GDK.
+ * Update the internal state as if the user had clicked outside the
+ * menu
+ */
+ if (event->window_state.new_window_state & GDK_WINDOW_STATE_WITHDRAWN &&
+ event->window_state.changed_mask & GDK_WINDOW_STATE_WITHDRAWN)
+ gtk_menu_shell_deactivate (GTK_MENU_SHELL(menu));
+ break;
default:
break;
}
priv->needs_destruction_ref = TRUE;
priv->monitor_num = -1;
+ priv->drag_start_y = -1;
context = gtk_widget_get_style_context (GTK_WIDGET (menu));
gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENU);
+
+ _gtk_widget_set_captured_event_handler (GTK_WIDGET (menu), gtk_menu_captured_event);
}
static void
}
/**
- * gtk_menu_attach_to_widget: (skip)
+ * gtk_menu_attach_to_widget:
* @menu: a #GtkMenu
* @attach_widget: the #GtkWidget that the menu will be attached to
- * @detacher: the user supplied callback function that will be called
- * when the menu calls gtk_menu_detach()
+ * @detacher: (scope async)(allow-none): the user supplied callback function
+ * that will be called when the menu calls gtk_menu_detach()
*
* Attaches the menu to the widget and provides a callback function
* that will be invoked when the menu calls gtk_menu_detach() during
g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list,
(GDestroyNotify) g_list_free);
- if (gtk_widget_get_state_flags (GTK_WIDGET (menu)) != 0)
- gtk_widget_set_state_flags (GTK_WIDGET (menu), 0, TRUE);
+ /* Attach the widget to the toplevel window. */
+ gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), attach_widget);
- /* we don't need to set the style here, since
- * we are a toplevel widget.
- */
+ _gtk_widget_update_parent_muxer (GTK_WIDGET (menu));
/* Fallback title for menu comes from attach widget */
gtk_menu_update_title (menu);
}
g_object_set_data (G_OBJECT (menu), I_(attach_data_key), NULL);
+ /* Detach the toplevel window. */
+ gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), NULL);
+
g_signal_handlers_disconnect_by_func (data->attach_widget,
(gpointer) attach_widget_screen_changed,
menu);
g_slice_free (GtkMenuAttachData, data);
+ _gtk_widget_update_parent_muxer (GTK_WIDGET (menu));
+
/* Fallback title for menu comes from attach widget */
gtk_menu_update_title (menu);
+ g_object_notify (G_OBJECT (menu), "attach-widget");
g_object_unref (menu);
}
if (pointer &&
gdk_device_grab (pointer, window,
GDK_OWNERSHIP_WINDOW, TRUE,
+ GDK_SMOOTH_SCROLL_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
GDK_POINTER_MOTION_MASK,
GtkMenuShell *menu_shell;
gboolean grab_keyboard;
GtkWidget *parent_toplevel;
- GdkDevice *keyboard, *pointer;
+ GdkDevice *keyboard, *pointer, *source_device = NULL;
g_return_if_fail (GTK_IS_MENU (menu));
g_return_if_fail (device == NULL || GDK_IS_DEVICE (device));
(current_event->type != GDK_ENTER_NOTIFY))
menu_shell->priv->ignore_enter = TRUE;
+ source_device = gdk_event_get_source_device (current_event);
gdk_event_free (current_event);
}
else
/* Compute the size of the toplevel and realize it so we
* can scroll correctly.
*/
+ if (!gtk_widget_get_realized (GTK_WIDGET (menu)))
{
GtkRequisition tmp_request;
GtkAllocation tmp_allocation = { 0, };
gtk_widget_size_allocate (priv->toplevel, &tmp_allocation);
- gtk_widget_realize (GTK_WIDGET (menu));
+ gtk_widget_realize (priv->toplevel);
}
gtk_menu_scroll_to (menu, priv->scroll_offset);
/* if no item is selected, select the first one */
- if (!menu_shell->priv->active_menu_item)
- {
- gboolean touchscreen_mode;
-
- g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
-
- if (touchscreen_mode)
- gtk_menu_shell_select_first (menu_shell, TRUE);
- }
+ if (!menu_shell->priv->active_menu_item &&
+ source_device && gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
+ gtk_menu_shell_select_first (menu_shell, TRUE);
/* Once everything is set up correctly, map the toplevel */
gtk_widget_show (priv->toplevel);
}
/**
- * gtk_menu_popup: (skip)
+ * gtk_menu_popup:
* @menu: a #GtkMenu
* @parent_menu_shell: (allow-none): the menu shell containing the
* triggering menu item, or %NULL
* @parent_menu_item: (allow-none): the menu item whose activation
* triggered the popup, or %NULL
- * @func: (allow-none): a user supplied function used to position
+ * @func: (scope async) (allow-none): a user supplied function used to position
* the menu, or %NULL
* @data: user supplied data to be passed to @func.
* @button: the mouse button which was pressed to initiate the event.
* @menu: a #GtkMenu
*
* Returns the selected menu item from the menu. This is used by the
- * #GtkOptionMenu.
+ * #GtkComboBox.
*
* Returns: (transfer none): the #GtkMenuItem that was last selected
* in the menu. If a selection has not yet been made, the
* from 0 to n-1
*
* Selects the specified menu item within the menu. This is used by
- * the #GtkOptionMenu and should not be used by anyone else.
+ * the #GtkComboBox and should not be used by anyone else.
*/
void
gtk_menu_set_active (GtkMenu *menu,
}
/**
- * gtk_menu_set_accel_path
+ * gtk_menu_set_accel_path:
* @menu: a valid #GtkMenu
* @accel_path: (allow-none): a valid accelerator path
*
}
/**
- * gtk_menu_get_accel_path
+ * gtk_menu_get_accel_path:
* @menu: a valid #GtkMenu
*
* Retrieves the accelerator path set on the menu.
* has no title set on it. This string is owned by GTK+
* and should not be modified or freed.
**/
-G_CONST_RETURN gchar *
+const gchar *
gtk_menu_get_title (GtkMenu *menu)
{
g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
}
static void
-get_menu_border (GtkWidget *widget,
- GtkBorder *border)
+get_menu_padding (GtkWidget *widget,
+ GtkBorder *padding)
{
GtkStyleContext *context;
GtkStateFlags state;
- GtkBorder padding, border_width;
context = gtk_widget_get_style_context (widget);
state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get_padding (context, state, &padding);
- gtk_style_context_get_border (context, state, &border_width);
-
- border->left = border_width.left + padding.left;
- border->right = border_width.right + padding.right;
- border->top = border_width.top + padding.top;
- border->bottom = border_width.bottom + padding.bottom;
+ gtk_style_context_get_padding (context, state, padding);
}
static void
gint border_width;
GtkWidget *child;
GList *children;
- guint vertical_padding;
- guint horizontal_padding;
- GtkBorder arrow_border, border;
+ GtkBorder arrow_border, padding;
g_return_if_fail (GTK_IS_MENU (widget));
window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gtk_widget_set_window (widget, window);
- gdk_window_set_user_data (window, widget);
+ gtk_widget_register_window (widget, window);
- get_menu_border (widget, &border);
+ get_menu_padding (widget, &padding);
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
context = gtk_widget_get_style_context (widget);
- gtk_widget_style_get (GTK_WIDGET (menu),
- "vertical-padding", &vertical_padding,
- "horizontal-padding", &horizontal_padding,
- NULL);
-
gtk_widget_get_allocation (widget, &allocation);
- attributes.x = border_width + border.left + horizontal_padding;
- attributes.y = border_width + border.top + vertical_padding;
+ attributes.x = border_width + padding.left;
+ attributes.y = border_width + padding.top;
attributes.width = allocation.width -
- (2 * (border_width + horizontal_padding)) - border.left - border.right;
+ (2 * border_width) - padding.left - padding.right;
attributes.height = allocation.height -
- (2 * (border_width + vertical_padding)) - border.top - border.bottom;
+ (2 * border_width) - padding.top - padding.bottom;
get_arrows_border (menu, &arrow_border);
attributes.y += arrow_border.top;
priv->view_window = gdk_window_new (window,
&attributes, attributes_mask);
- gdk_window_set_user_data (priv->view_window, menu);
+ gtk_widget_register_window (widget, priv->view_window);
gtk_widget_get_allocation (widget, &allocation);
attributes.x = 0;
attributes.y = 0;
- attributes.width = allocation.width + (2 * (border_width + horizontal_padding)) +
- border.left + border.right;
- attributes.height = priv->requested_height - (2 * (border_width + vertical_padding)) +
- border.top + border.bottom;
+ attributes.width = allocation.width + (2 * border_width) +
+ padding.left + padding.right;
+ attributes.height = priv->requested_height - (2 * border_width) +
+ padding.top + padding.bottom;
attributes.width = MAX (1, attributes.width);
attributes.height = MAX (1, attributes.height);
priv->bin_window = gdk_window_new (priv->view_window,
&attributes, attributes_mask);
- gdk_window_set_user_data (priv->bin_window, menu);
+ gtk_widget_register_window (widget, priv->bin_window);
children = GTK_MENU_SHELL (menu)->priv->children;
while (children)
window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)),
&attributes, attributes_mask);
- gdk_window_set_user_data (window, menu);
+ gtk_widget_register_window (GTK_WIDGET (menu), window);
gdk_window_show (window);
GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
if (window)
{
- gdk_window_set_user_data (window, NULL);
+ gtk_widget_unregister_window (GTK_WIDGET (menu), window);
gdk_window_destroy (window);
g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), NULL);
}
menu_grab_transfer_window_destroy (menu);
- gdk_window_set_user_data (priv->view_window, NULL);
+ gtk_widget_unregister_window (widget, priv->view_window);
gdk_window_destroy (priv->view_window);
priv->view_window = NULL;
- gdk_window_set_user_data (priv->bin_window, NULL);
+ gtk_widget_unregister_window (widget, priv->bin_window);
gdk_window_destroy (priv->bin_window);
priv->bin_window = NULL;
guint **ret_min_heights,
guint **ret_nat_heights)
{
- GtkStyleContext *context;
- GtkStateFlags state;
GtkBorder padding;
GtkMenuPrivate *priv;
GtkMenuShell *menu_shell;
GtkWidget *child, *widget;
GList *children;
- guint horizontal_padding;
guint border_width;
guint n_columns;
gint n_heights;
n_columns = gtk_menu_get_n_columns (menu);
avail_width = for_width - (2 * priv->toggle_size + priv->accel_size) * n_columns;
- gtk_widget_style_get (GTK_WIDGET (menu),
- "horizontal-padding", &horizontal_padding,
- NULL);
-
- context = gtk_widget_get_style_context (widget);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get_padding (context, state, &padding);
+ get_menu_padding (widget, &padding);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
- avail_width -= (border_width + horizontal_padding) * 2 + padding.left + padding.right;
+ avail_width -= (border_width) * 2 + padding.left + padding.right;
for (children = menu_shell->priv->children; children; children = children->next)
{
gint x, y, i;
gint width, height;
guint border_width;
- guint vertical_padding;
- guint horizontal_padding;
- GtkBorder border;
+ GtkBorder padding;
g_return_if_fail (GTK_IS_MENU (widget));
g_return_if_fail (allocation != NULL);
gtk_widget_set_allocation (widget, allocation);
- gtk_widget_style_get (GTK_WIDGET (menu),
- "vertical-padding", &vertical_padding,
- "horizontal-padding", &horizontal_padding,
- NULL);
-
- get_menu_border (widget, &border);
+ get_menu_padding (widget, &padding);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
g_free (priv->heights);
NULL);
/* refresh our cached height request */
- priv->requested_height = (2 * (border_width + vertical_padding)) +
- border.top + border.bottom;
+ priv->requested_height = (2 * border_width) + padding.top + padding.bottom;
for (i = 0; i < priv->heights_length; i++)
priv->requested_height += priv->heights[i];
- x = border_width + border.left + horizontal_padding;
- y = border_width + border.top + vertical_padding;
- width = allocation->width - (2 * (border_width + horizontal_padding)) -
- border.left - border.right;
- height = allocation->height - (2 * (border_width + vertical_padding)) -
- border.top - border.bottom;
+ x = border_width + padding.left;
+ y = border_width + padding.top;
+ width = allocation->width - (2 * border_width) -
+ padding.left - padding.right;
+ height = allocation->height - (2 * border_width) -
+ padding.top - padding.bottom;
if (menu_shell->priv->active)
gtk_menu_scroll_to (menu, priv->scroll_offset);
GtkArrowPlacement arrow_placement;
GtkWidget *widget = GTK_WIDGET (menu);
guint border_width;
- guint vertical_padding;
- guint horizontal_padding;
gint scroll_arrow_height;
- GtkBorder menu_border;
+ GtkBorder menu_padding;
gtk_widget_style_get (widget,
- "vertical-padding", &vertical_padding,
- "horizontal-padding", &horizontal_padding,
"scroll-arrow-vlength", &scroll_arrow_height,
"arrow-placement", &arrow_placement,
NULL);
- get_menu_border (widget, &menu_border);
+ get_menu_padding (widget, &menu_padding);
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
- border->x = border_width + menu_border.left + horizontal_padding;
- border->y = border_width + menu_border.top + vertical_padding;
+ border->x = border_width + menu_padding.left;
+ border->y = border_width + menu_padding.top;
border->width = gdk_window_get_width (gtk_widget_get_window (widget));
border->height = gdk_window_get_height (gtk_widget_get_window (widget));
lower->x = lower->y = lower->width = lower->height = 0;
}
- *arrow_space = scroll_arrow_height - menu_border.top - menu_border.bottom;
+ *arrow_space = scroll_arrow_height - menu_padding.top - menu_padding.bottom;
}
static gboolean
GdkRectangle upper;
GdkRectangle lower;
gint arrow_space;
- GtkBorder menu_border;
+ GtkBorder menu_padding;
menu = GTK_MENU (widget);
priv = menu->priv;
context = gtk_widget_get_style_context (widget);
get_arrows_visible_area (menu, &border, &upper, &lower, &arrow_space);
- get_menu_border (widget, &menu_border);
+ get_menu_padding (widget, &menu_padding);
if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
{
gtk_render_arrow (context, cr, 0,
upper.x + (upper.width - arrow_size) / 2,
- upper.y + menu_border.top + (arrow_space - arrow_size) / 2,
+ upper.y + menu_padding.top + (arrow_space - arrow_size) / 2,
arrow_size);
gtk_style_context_restore (context);
gtk_render_arrow (context, cr, G_PI,
lower.x + (lower.width - arrow_size) / 2,
- lower.y + menu_border.top + (arrow_space - arrow_size) / 2,
+ lower.y + menu_padding.bottom + (arrow_space - arrow_size) / 2,
arrow_size);
gtk_style_context_restore (context);
GList *children;
guint max_toggle_size;
guint max_accel_width;
- guint horizontal_padding;
guint border_width;
gint child_min, child_nat;
gint min_width, nat_width;
- GtkBorder border;
+ GtkBorder padding;
menu = GTK_MENU (widget);
menu_shell = GTK_MENU_SHELL (widget);
!priv->no_toggle_size)
{
GtkStyleContext *context;
- GtkWidgetPath *menu_path, *check_path;
+ GtkWidgetPath *check_path;
guint toggle_spacing;
guint indicator_size;
- context = gtk_widget_get_style_context (widget);
- menu_path = gtk_widget_path_copy (gtk_style_context_get_path (context));
+ context = gtk_style_context_new ();
/* Create a GtkCheckMenuItem path, only to query indicator spacing */
- check_path = gtk_widget_path_copy (menu_path);
+ check_path = _gtk_widget_create_path (widget);
gtk_widget_path_append_type (check_path, GTK_TYPE_CHECK_MENU_ITEM);
gtk_style_context_set_path (context, check_path);
gtk_widget_path_free (check_path);
+ gtk_style_context_set_screen (context, gtk_widget_get_screen (widget));
gtk_style_context_get_style (context,
"toggle-spacing", &toggle_spacing,
max_toggle_size = indicator_size + toggle_spacing;
- /* Restore real widget path */
- gtk_style_context_set_path (context, menu_path);
- gtk_widget_path_free (menu_path);
+ g_object_unref (context);
}
min_width += 2 * max_toggle_size + max_accel_width;
nat_width += 2 * max_toggle_size + max_accel_width;
nat_width *= gtk_menu_get_n_columns (menu);
- gtk_widget_style_get (GTK_WIDGET (menu),
- "horizontal-padding", &horizontal_padding,
- NULL);
-
- get_menu_border (widget, &border);
+ get_menu_padding (widget, &padding);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
- min_width += (2 * (border_width + horizontal_padding)) +
- border.left + border.right;
- nat_width += (2 * (border_width + horizontal_padding)) +
- border.top + border.bottom;
+ min_width += (2 * border_width) + padding.left + padding.right;
+ nat_width += (2 * border_width) + padding.left + padding.right;
priv->toggle_size = max_toggle_size;
priv->accel_size = max_accel_width;
gint *minimum_size,
gint *natural_size)
{
- GtkStyleContext *context;
- GtkStateFlags state;
- GtkBorder padding, border;
+ GtkBorder padding;
GtkMenu *menu = GTK_MENU (widget);
GtkMenuPrivate *priv = menu->priv;
guint *min_heights, *nat_heights;
- guint vertical_padding, border_width;
+ guint border_width;
gint n_heights, i;
gint min_height, nat_height;
- gtk_widget_style_get (widget, "vertical-padding", &vertical_padding, NULL);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
+ get_menu_padding (widget, &padding);
- context = gtk_widget_get_style_context (widget);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get_padding (context, state, &padding);
- gtk_style_context_get_border (context, state, &border);
-
- min_height = nat_height = (border_width + vertical_padding) * 2 +
- padding.left + padding.right + border.left + border.right;
+ min_height = nat_height = (2 * border_width) + padding.top + padding.bottom;
n_heights =
calculate_line_heights (menu, for_size, &min_heights, &nat_heights);
GdkScreen *screen = gtk_widget_get_screen (priv->toplevel);
GdkRectangle monitor;
- gdk_screen_get_monitor_geometry (screen, priv->monitor_num, &monitor);
+ gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
if (priv->position_y + min_height > monitor.y + monitor.height)
min_height = monitor.y + monitor.height - priv->position_y;
g_free (nat_heights);
}
-
-
-static gboolean
-gtk_menu_button_scroll (GtkMenu *menu,
- GdkEventButton *event)
-{
- GtkMenuPrivate *priv = menu->priv;
-
- if (priv->upper_arrow_prelight || priv->lower_arrow_prelight)
- {
- gboolean touchscreen_mode;
-
- g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
-
- if (touchscreen_mode)
- gtk_menu_handle_scrolling (menu,
- event->x_root, event->y_root,
- event->type == GDK_BUTTON_PRESS,
- FALSE);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
static gboolean
pointer_in_menu_window (GtkWidget *widget,
gdouble x_root,
gtk_menu_button_press (GtkWidget *widget,
GdkEventButton *event)
{
+ GdkDevice *source_device;
+ GtkWidget *event_widget;
+ GtkMenu *menu;
+
if (event->type != GDK_BUTTON_PRESS)
return FALSE;
- /* Don't pass down to menu shell for presses over scroll arrows
- */
- if (gtk_menu_button_scroll (GTK_MENU (widget), event))
- return TRUE;
+ source_device = gdk_event_get_source_device ((GdkEvent *) event);
+ event_widget = gtk_get_event_widget ((GdkEvent *) event);
+ menu = GTK_MENU (widget);
/* Don't pass down to menu shell if a non-menuitem part of the menu
* was clicked. The check for the event_widget being a GtkMenuShell
* the menu or on its border are delivered relative to
* menu_shell->window.
*/
- if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
+ if (GTK_IS_MENU_SHELL (event_widget) &&
pointer_in_menu_window (widget, event->x_root, event->y_root))
return TRUE;
+ if (GTK_IS_MENU_ITEM (event_widget) &&
+ gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
+ GTK_MENU_ITEM (event_widget)->priv->submenu != NULL &&
+ !gtk_widget_is_drawable (GTK_MENU_ITEM (event_widget)->priv->submenu))
+ menu->priv->ignore_button_release = TRUE;
+
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
}
if (event->type != GDK_BUTTON_RELEASE)
return FALSE;
- /* Don't pass down to menu shell for releases over scroll arrows
- */
- if (gtk_menu_button_scroll (GTK_MENU (widget), event))
- return TRUE;
-
/* Don't pass down to menu shell if a non-menuitem part of the menu
* was clicked (see comment in button_press()).
*/
}
/* Figure out what modifiers went into determining the key symbol */
- gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (display),
+ _gtk_translate_keyboard_accel_state (gdk_keymap_get_for_display (display),
event->hardware_keycode,
- event->state, event->group,
- NULL, NULL, NULL, &consumed_modifiers);
+ event->state,
+ gtk_accelerator_get_default_mod_mask (),
+ event->group,
+ &accel_key, NULL, NULL, &consumed_modifiers);
- accel_key = gdk_keyval_to_lower (event->keyval);
+ accel_key = gdk_keyval_to_lower (accel_key);
accel_mods = event->state & gtk_accelerator_get_default_mod_mask () & ~consumed_modifiers;
/* If lowercasing affects the keysym, then we need to include SHIFT
- * in the modifiers, we re-uppercase when we match against the keyval,
- * but display and save in caseless form.
+ * in the modifiers, We re-upper case when we match against the
+ * keyval, but display and save in caseless form.
*/
if (accel_key != event->keyval)
accel_mods |= GDK_SHIFT_MASK;
GtkMenu *menu;
GtkMenuShell *menu_shell;
GtkWidget *parent;
+ GdkDevice *source_device;
gboolean need_enter;
- if (GTK_IS_MENU (widget))
+ source_device = gdk_event_get_source_device ((GdkEvent *) event);
+
+ if (GTK_IS_MENU (widget) &&
+ gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
{
GtkMenuPrivate *priv = GTK_MENU(widget)->priv;
gtk_menu_scroll_to (menu, offset);
}
-static void
-gtk_menu_do_timeout_scroll (GtkMenu *menu,
- gboolean touchscreen_mode)
-{
- GtkMenuPrivate *priv = menu->priv;
- gboolean upper_visible;
- gboolean lower_visible;
-
- upper_visible = priv->upper_arrow_visible;
- lower_visible = priv->lower_arrow_visible;
-
- gtk_menu_scroll_by (menu, priv->scroll_step);
-
- if (touchscreen_mode &&
- (upper_visible != priv->upper_arrow_visible ||
- lower_visible != priv->lower_arrow_visible))
- {
- /* We are about to hide a scroll arrow while the mouse is pressed,
- * this would cause the uncovered menu item to be activated on button
- * release. Therefore we need to ignore button release here
- */
- GTK_MENU_SHELL (menu)->priv->ignore_enter = TRUE;
- priv->ignore_button_release = TRUE;
- }
-}
-
static gboolean
gtk_menu_scroll_timeout (gpointer data)
{
GtkMenu *menu;
- gboolean touchscreen_mode;
menu = GTK_MENU (data);
-
- g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
-
- gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
+ gtk_menu_scroll_by (menu, menu->priv->scroll_step);
return TRUE;
}
-static gboolean
-gtk_menu_scroll_timeout_initial (gpointer data)
-{
- GtkMenu *menu;
- guint timeout;
- gboolean touchscreen_mode;
-
- menu = GTK_MENU (data);
-
- g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
- "gtk-timeout-repeat", &timeout,
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
-
- gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
-
- gtk_menu_remove_scroll_timeout (menu);
-
- menu->priv->scroll_timeout =
- gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout, menu);
-
- return FALSE;
-}
-
-static void
-gtk_menu_start_scrolling (GtkMenu *menu)
-{
- guint timeout;
- gboolean touchscreen_mode;
-
- g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
- "gtk-timeout-repeat", &timeout,
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
-
- gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
-
- menu->priv->scroll_timeout =
- gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout_initial, menu);
-}
-
static gboolean
gtk_menu_scroll (GtkWidget *widget,
GdkEventScroll *event)
case GDK_SCROLL_UP:
gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2);
break;
+ case GDK_SCROLL_SMOOTH:
+ gtk_menu_scroll_by (menu, event->delta_y);
+ break;
}
return TRUE;
GdkWindow *window;
gint width, height;
guint border;
- guint vertical_padding;
gint win_x, win_y;
gint scroll_arrow_height;
- GtkStyleContext *context;
- GtkStateFlags state;
GtkBorder padding;
window = gtk_widget_get_window (widget);
height = gdk_window_get_height (window);
gtk_widget_style_get (widget,
- "vertical-padding", &vertical_padding,
"scroll-arrow-vlength", &scroll_arrow_height,
"arrow-placement", &arrow_placement,
NULL);
- border = gtk_container_get_border_width (GTK_CONTAINER (menu)) + vertical_padding;
-
- context = gtk_widget_get_style_context (widget);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get_padding (context, state, &padding);
+ border = gtk_container_get_border_width (GTK_CONTAINER (menu));
+ get_menu_padding (widget, &padding);
gdk_window_get_position (window, &win_x, &win_y);
gboolean in_arrow;
gboolean scroll_fast = FALSE;
gint top_x, top_y;
- gboolean touchscreen_mode;
menu_shell = GTK_MENU_SHELL (menu);
- g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
-
gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
&top_x, &top_y);
x -= top_x;
in_arrow = TRUE;
}
- if (touchscreen_mode)
- priv->upper_arrow_prelight = in_arrow;
-
if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
gboolean arrow_pressed = FALSE;
if (priv->upper_arrow_visible && !priv->tearoff_active)
{
- if (touchscreen_mode)
+ scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
+
+ if (enter && in_arrow &&
+ (!priv->upper_arrow_prelight ||
+ priv->scroll_fast != scroll_fast))
{
- if (enter && priv->upper_arrow_prelight)
- {
- if (priv->scroll_timeout == 0)
- {
- /* Deselect the active item so that
- * any submenus are popped down
- */
- gtk_menu_shell_deselect (menu_shell);
-
- gtk_menu_remove_scroll_timeout (menu);
- priv->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
-
- if (!motion)
- {
- /* Only do stuff on click. */
- gtk_menu_start_scrolling (menu);
- arrow_pressed = TRUE;
- }
- }
- else
- {
- arrow_pressed = TRUE;
- }
- }
- else if (!enter)
- {
- gtk_menu_stop_scrolling (menu);
- }
+ priv->upper_arrow_prelight = TRUE;
+ priv->scroll_fast = scroll_fast;
+
+ /* Deselect the active item so that
+ * any submenus are popped down
+ */
+ gtk_menu_shell_deselect (menu_shell);
+
+ gtk_menu_remove_scroll_timeout (menu);
+ priv->scroll_step = scroll_fast
+ ? -MENU_SCROLL_STEP2
+ : -MENU_SCROLL_STEP1;
+
+ priv->scroll_timeout =
+ gdk_threads_add_timeout (scroll_fast
+ ? MENU_SCROLL_TIMEOUT2
+ : MENU_SCROLL_TIMEOUT1,
+ gtk_menu_scroll_timeout, menu);
}
- else /* !touchscreen_mode */
+ else if (!enter && !in_arrow && priv->upper_arrow_prelight)
{
- scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
-
- if (enter && in_arrow &&
- (!priv->upper_arrow_prelight ||
- priv->scroll_fast != scroll_fast))
- {
- priv->upper_arrow_prelight = TRUE;
- priv->scroll_fast = scroll_fast;
-
- /* Deselect the active item so that
- * any submenus are popped down
- */
- gtk_menu_shell_deselect (menu_shell);
-
- gtk_menu_remove_scroll_timeout (menu);
- priv->scroll_step = scroll_fast
- ? -MENU_SCROLL_STEP2
- : -MENU_SCROLL_STEP1;
-
- priv->scroll_timeout =
- gdk_threads_add_timeout (scroll_fast
- ? MENU_SCROLL_TIMEOUT2
- : MENU_SCROLL_TIMEOUT1,
- gtk_menu_scroll_timeout, menu);
- }
- else if (!enter && !in_arrow && priv->upper_arrow_prelight)
- {
- gtk_menu_stop_scrolling (menu);
- }
+ gtk_menu_stop_scrolling (menu);
}
}
- /* gtk_menu_start_scrolling() might have hit the top of the
- * menu, so check if the button isn't insensitive before
+ /* check if the button isn't insensitive before
* changing it to something else.
*/
if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
in_arrow = TRUE;
}
- if (touchscreen_mode)
- priv->lower_arrow_prelight = in_arrow;
-
if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
gboolean arrow_pressed = FALSE;
if (priv->lower_arrow_visible && !priv->tearoff_active)
{
- if (touchscreen_mode)
+ scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
+
+ if (enter && in_arrow &&
+ (!priv->lower_arrow_prelight ||
+ priv->scroll_fast != scroll_fast))
{
- if (enter && priv->lower_arrow_prelight)
- {
- if (priv->scroll_timeout == 0)
- {
- /* Deselect the active item so that
- * any submenus are popped down
- */
- gtk_menu_shell_deselect (menu_shell);
-
- gtk_menu_remove_scroll_timeout (menu);
- priv->scroll_step = MENU_SCROLL_STEP2; /* always fast */
-
- if (!motion)
- {
- /* Only do stuff on click. */
- gtk_menu_start_scrolling (menu);
- arrow_pressed = TRUE;
- }
- }
- else
- {
- arrow_pressed = TRUE;
- }
- }
- else if (!enter)
- {
- gtk_menu_stop_scrolling (menu);
- }
+ priv->lower_arrow_prelight = TRUE;
+ priv->scroll_fast = scroll_fast;
+
+ /* Deselect the active item so that
+ * any submenus are popped down
+ */
+ gtk_menu_shell_deselect (menu_shell);
+
+ gtk_menu_remove_scroll_timeout (menu);
+ priv->scroll_step = scroll_fast
+ ? MENU_SCROLL_STEP2
+ : MENU_SCROLL_STEP1;
+
+ priv->scroll_timeout =
+ gdk_threads_add_timeout (scroll_fast
+ ? MENU_SCROLL_TIMEOUT2
+ : MENU_SCROLL_TIMEOUT1,
+ gtk_menu_scroll_timeout, menu);
}
- else /* !touchscreen_mode */
+ else if (!enter && !in_arrow && priv->lower_arrow_prelight)
{
- scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
-
- if (enter && in_arrow &&
- (!priv->lower_arrow_prelight ||
- priv->scroll_fast != scroll_fast))
- {
- priv->lower_arrow_prelight = TRUE;
- priv->scroll_fast = scroll_fast;
-
- /* Deselect the active item so that
- * any submenus are popped down
- */
- gtk_menu_shell_deselect (menu_shell);
-
- gtk_menu_remove_scroll_timeout (menu);
- priv->scroll_step = scroll_fast
- ? MENU_SCROLL_STEP2
- : MENU_SCROLL_STEP1;
-
- priv->scroll_timeout =
- gdk_threads_add_timeout (scroll_fast
- ? MENU_SCROLL_TIMEOUT2
- : MENU_SCROLL_TIMEOUT1,
- gtk_menu_scroll_timeout, menu);
- }
- else if (!enter && !in_arrow && priv->lower_arrow_prelight)
- {
- gtk_menu_stop_scrolling (menu);
- }
+ gtk_menu_stop_scrolling (menu);
}
}
- /* gtk_menu_start_scrolling() might have hit the bottom of the
- * menu, so check if the button isn't insensitive before
+ /* check if the button isn't insensitive before
* changing it to something else.
*/
if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
GtkWidget *menu_item;
GtkWidget *parent;
- gboolean touchscreen_mode;
+ GdkDevice *source_device;
if (event->mode == GDK_CROSSING_GTK_GRAB ||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
event->mode == GDK_CROSSING_STATE_CHANGED)
return TRUE;
- g_object_get (gtk_widget_get_settings (widget),
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
-
+ source_device = gdk_event_get_source_device ((GdkEvent *) event);
menu_item = gtk_get_event_widget ((GdkEvent*) event);
- if (GTK_IS_MENU (widget))
+
+ if (GTK_IS_MENU (widget) &&
+ gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
{
GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
event->x_root, event->y_root, TRUE, TRUE);
}
- if (!touchscreen_mode && GTK_IS_MENU_ITEM (menu_item))
+ if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN &&
+ GTK_IS_MENU_ITEM (menu_item))
{
GtkWidget *menu = gtk_widget_get_parent (menu_item);
GtkMenu *menu;
GtkMenuItem *menu_item;
GtkWidget *event_widget;
+ GdkDevice *source_device;
if (event->mode == GDK_CROSSING_GTK_GRAB ||
event->mode == GDK_CROSSING_GTK_UNGRAB ||
if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
return TRUE;
- gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
+ source_device = gdk_event_get_source_device ((GdkEvent *) event);
+
+ if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
+ gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
event_widget = gtk_get_event_widget ((GdkEvent*) event);
return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event);
}
+static gboolean
+pointer_on_menu_widget (GtkMenu *menu,
+ gdouble x_root,
+ gdouble y_root)
+{
+ GtkMenuPrivate *priv = menu->priv;
+ GtkAllocation allocation;
+ gint window_x, window_y;
+
+ gtk_widget_get_allocation (GTK_WIDGET (menu), &allocation);
+ gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
+ &window_x, &window_y);
+
+ if (x_root >= window_x && x_root < window_x + allocation.width &&
+ y_root >= window_y && y_root < window_y + allocation.height)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+gtk_menu_captured_event (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkDevice *source_device;
+ gboolean retval = FALSE;
+ GtkMenuPrivate *priv;
+ GtkMenu *menu;
+ gdouble x_root, y_root;
+ guint button;
+ GdkModifierType state;
+
+ menu = GTK_MENU (widget);
+ priv = menu->priv;
+
+ if (!priv->upper_arrow_visible && !priv->lower_arrow_visible && priv->drag_start_y < 0)
+ return retval;
+
+ source_device = gdk_event_get_source_device (event);
+ gdk_event_get_root_coords (event, &x_root, &y_root);
+
+ switch (event->type)
+ {
+ case GDK_TOUCH_BEGIN:
+ case GDK_BUTTON_PRESS:
+ if ((!gdk_event_get_button (event, &button) || button == 1) &&
+ gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
+ pointer_on_menu_widget (menu, x_root, y_root))
+ {
+ priv->drag_start_y = event->button.y_root;
+ priv->initial_drag_offset = priv->scroll_offset;
+ priv->drag_scroll_started = FALSE;
+ }
+ else
+ priv->drag_start_y = -1;
+
+ priv->drag_already_pressed = TRUE;
+ break;
+ case GDK_TOUCH_END:
+ case GDK_BUTTON_RELEASE:
+ if (priv->drag_scroll_started)
+ {
+ priv->drag_scroll_started = FALSE;
+ priv->drag_start_y = -1;
+ priv->drag_already_pressed = FALSE;
+ retval = TRUE;
+ }
+ break;
+ case GDK_TOUCH_UPDATE:
+ case GDK_MOTION_NOTIFY:
+ if ((!gdk_event_get_state (event, &state) || (state & GDK_BUTTON1_MASK) != 0) &&
+ gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
+ {
+ if (!priv->drag_already_pressed)
+ {
+ if (pointer_on_menu_widget (menu, x_root, y_root))
+ {
+ priv->drag_start_y = y_root;
+ priv->initial_drag_offset = priv->scroll_offset;
+ priv->drag_scroll_started = FALSE;
+ }
+ else
+ priv->drag_start_y = -1;
+
+ priv->drag_already_pressed = TRUE;
+ }
+
+ if (priv->drag_start_y < 0 && !priv->drag_scroll_started)
+ break;
+
+ if (priv->drag_scroll_started)
+ {
+ gint offset, view_height;
+ GtkBorder arrow_border;
+ gdouble y_diff;
+
+ y_diff = y_root - priv->drag_start_y;
+ offset = priv->initial_drag_offset - y_diff;
+
+ view_height = gdk_window_get_height (gtk_widget_get_window (widget));
+ get_arrows_border (menu, &arrow_border);
+
+ if (priv->upper_arrow_visible)
+ view_height -= arrow_border.top;
+
+ if (priv->lower_arrow_visible)
+ view_height -= arrow_border.bottom;
+
+ offset = CLAMP (offset,
+ MIN (priv->scroll_offset, 0),
+ MAX (priv->scroll_offset, priv->requested_height - view_height));
+
+ gtk_menu_scroll_to (menu, offset);
+
+ retval = TRUE;
+ }
+ else if (gtk_drag_check_threshold (widget,
+ 0, priv->drag_start_y,
+ 0, y_root))
+ {
+ priv->drag_scroll_started = TRUE;
+ gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
+ retval = TRUE;
+ }
+ }
+ break;
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ if (priv->drag_scroll_started)
+ retval = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return retval;
+}
+
static void
gtk_menu_stop_navigating_submenu (GtkMenu *menu)
{
GtkRequisition requisition;
gint x, y;
gint scroll_offset;
- gint menu_height;
GdkScreen *screen;
GdkScreen *pointer_screen;
GdkRectangle monitor;
pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (menu));
gdk_device_get_position (pointer, &pointer_screen, &x, &y);
- /* Get the minimum height for minimum width to figure out
+ /* Realize so we have the proper width and heigh to figure out
* the right place to popup the menu.
*/
- gtk_widget_get_preferred_size (widget, &requisition, NULL);
+ gtk_widget_realize (priv->toplevel);
+ requisition.width = gtk_widget_get_allocated_width (widget);
+ requisition.height = gtk_widget_get_allocated_height (widget);
if (pointer_screen != screen)
{
if (priv->monitor_num < 0)
priv->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
- gdk_screen_get_monitor_geometry (screen, priv->monitor_num, &monitor);
+ gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
}
else
{
gint space_left, space_right, space_above, space_below;
gint needed_width;
gint needed_height;
- GtkBorder border;
+ GtkBorder padding;
gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
- get_menu_border (widget, &border);
+ get_menu_padding (widget, &padding);
/* The placement of popup menus horizontally works like this (with
* RTL in parentheses)
* Positioning in the vertical direction is similar: first try below
* mouse cursor, then above.
*/
- gdk_screen_get_monitor_geometry (screen, priv->monitor_num, &monitor);
+ gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
space_left = x - monitor.x;
space_right = monitor.x + monitor.width - x - 1;
/* the amount of space we need to position the menu.
* Note the menu is offset "thickness" pixels
*/
- needed_width = requisition.width - border.left;
+ needed_width = requisition.width - padding.left;
if (needed_width <= space_left ||
needed_width <= space_right)
(!rtl && needed_width > space_right))
{
/* position left */
- x = x + border.left - requisition.width + 1;
+ x = x + padding.left - requisition.width + 1;
}
else
{
/* position right */
- x = x - border.right;
+ x = x - padding.right;
}
/* x is clamped on-screen further down */
* The algorithm is the same as above, but simpler
* because we don't have to take RTL into account.
*/
- needed_height = requisition.height - border.top;
+ needed_height = requisition.height - padding.top;
if (needed_height <= space_above ||
needed_height <= space_below)
{
if (needed_height <= space_below)
- y = y - border.top;
+ y = y - padding.top;
else
- y = y + border.bottom - requisition.height + 1;
+ y = y + padding.bottom - requisition.height + 1;
y = CLAMP (y, monitor.y,
monitor.y + monitor.height - requisition.height);
scroll_offset = 0;
- if (priv->initially_pushed_in)
+ if (y + requisition.height > monitor.y + monitor.height)
{
- menu_height = requisition.height;
-
- if (y + menu_height > monitor.y + monitor.height)
- {
- scroll_offset -= y + menu_height - (monitor.y + monitor.height);
- y = (monitor.y + monitor.height) - menu_height;
- }
+ if (priv->initially_pushed_in)
+ scroll_offset += (monitor.y + monitor.height) - requisition.height - y;
+ y = (monitor.y + monitor.height) - requisition.height;
+ }
- if (y < monitor.y)
- {
- scroll_offset += monitor.y - y;
- y = monitor.y;
- }
+ if (y < monitor.y)
+ {
+ if (priv->initially_pushed_in)
+ scroll_offset += monitor.y - y;
+ y = monitor.y;
}
- /* FIXME: should this be done in the various position_funcs ? */
x = CLAMP (x, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
if (GTK_MENU_SHELL (menu)->priv->active)
priv->position_y = y;
}
- if (y + requisition.height > monitor.y + monitor.height)
- requisition.height = (monitor.y + monitor.height) - y;
-
- if (y < monitor.y)
- {
- scroll_offset += monitor.y - y;
- requisition.height -= monitor.y - y;
- y = monitor.y;
- }
-
- if (scroll_offset > 0)
+ if (scroll_offset != 0)
{
GtkBorder arrow_border;
gtk_menu_stop_scrolling (GtkMenu *menu)
{
GtkMenuPrivate *priv = menu->priv;
- gboolean touchscreen_mode;
gtk_menu_remove_scroll_timeout (menu);
-
- g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
- "gtk-touchscreen-mode", &touchscreen_mode,
- NULL);
-
- if (!touchscreen_mode)
- {
- priv->upper_arrow_prelight = FALSE;
- priv->lower_arrow_prelight = FALSE;
- }
+ priv->upper_arrow_prelight = FALSE;
+ priv->lower_arrow_prelight = FALSE;
}
static void
gint offset)
{
GtkMenuPrivate *priv = menu->priv;
- GtkAllocation allocation;
- GtkBorder arrow_border, border;
+ GtkBorder arrow_border, padding;
GtkWidget *widget;
gint x, y;
gint view_width, view_height;
gint border_width;
gint menu_height;
- guint vertical_padding;
- guint horizontal_padding;
gboolean double_arrows;
widget = GTK_WIDGET (menu);
gtk_adjustment_set_value (priv->tearoff_adjustment, offset);
/* Move/resize the viewport according to arrows: */
- gtk_widget_get_allocation (widget, &allocation);
- view_width = allocation.width;
- view_height = allocation.height;
-
- gtk_widget_style_get (GTK_WIDGET (menu),
- "vertical-padding", &vertical_padding,
- "horizontal-padding", &horizontal_padding,
- NULL);
+ view_width = gtk_widget_get_allocated_width (widget);
+ view_height = gtk_widget_get_allocated_height (widget);
- get_menu_border (widget, &border);
+ get_menu_padding (widget, &padding);
double_arrows = get_double_arrows (menu);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
- view_width -= (2 * (border_width + horizontal_padding)) + border.left + border.right;
- view_height -= (2 * (border_width + vertical_padding)) + border.top + border.bottom;
- menu_height = priv->requested_height - (2 * (border_width + vertical_padding)) -
- border.top - border.bottom;
+ view_width -= (2 * border_width) + padding.left + padding.right;
+ view_height -= (2 * border_width) + padding.top + padding.bottom;
+ menu_height = priv->requested_height - (2 * border_width) - padding.top - padding.bottom;
- x = border_width + border.left + horizontal_padding;
- y = border_width + border.top + vertical_padding;
+ x = border_width + padding.left;
+ y = border_width + padding.top;
if (double_arrows && !priv->tearoff_active)
{
/* Scroll the menu: */
if (gtk_widget_get_realized (widget))
- gdk_window_move (priv->bin_window, 0, -offset);
-
- if (gtk_widget_get_realized (widget))
- gdk_window_move_resize (priv->view_window, x, y, view_width, view_height);
+ {
+ gdk_window_move (priv->bin_window, 0, -offset);
+ gdk_window_move_resize (priv->view_window, x, y, view_width, view_height);
+ }
priv->scroll_offset = offset;
}
if (!priv->heights || priv->heights_length < gtk_menu_get_n_rows (menu))
return FALSE;
- /* when we have a row with only invisible children, it's height will
+ /* when we have a row with only invisible children, its height will
* be zero, so there's no need to check WIDGET_VISIBLE here
*/
for (i = 0; i < item_top_attach; i++)
if (compute_child_offset (menu, menu_item,
&child_offset, &child_height, &last_child))
{
- guint vertical_padding;
gboolean double_arrows;
- GtkStyleContext *context;
- GtkStateFlags state;
GtkBorder padding;
y = priv->scroll_offset;
height = gdk_window_get_height (gtk_widget_get_window (widget));
- gtk_widget_style_get (widget,
- "vertical-padding", &vertical_padding,
- NULL);
-
double_arrows = get_double_arrows (menu);
-
- context = gtk_widget_get_style_context (widget);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get_padding (context, state, &padding);
+ get_menu_padding (widget, &padding);
height -= 2 * gtk_container_get_border_width (GTK_CONTAINER (menu)) +
- padding.top + padding.bottom +
- 2 * vertical_padding;
+ padding.top + padding.bottom;
+
if (child_offset < y)
{
/* Ignore the enter event we might get if the pointer
GtkAllocation allocation;
GtkWidget *widget = GTK_WIDGET (menu);
GtkContainer *container = GTK_CONTAINER (menu);
- GtkStyleContext *context;
- GtkStateFlags state;
GtkBorder padding;
gint menu_height;
gtk_widget_get_allocation (widget, &allocation);
-
- context = gtk_widget_get_style_context (widget);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get_padding (context, state, &padding);
+ get_menu_padding (widget, &padding);
menu_height = (allocation.height -
(2 * gtk_container_get_border_width (container)) -
get_menu_height (GtkMenu *menu)
{
GtkMenuPrivate *priv = menu->priv;
- GtkAllocation allocation;
GtkWidget *widget = GTK_WIDGET (menu);
- GtkStyleContext *context;
- GtkStateFlags state;
- GtkBorder padding, border;
+ GtkBorder padding;
gint height;
- gtk_widget_get_allocation (widget, &allocation);
+ get_menu_padding (widget, &padding);
- context = gtk_widget_get_style_context (widget);
- state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get_padding (context, state, &padding);
- gtk_style_context_get_border (context, state, &border);
-
- height = allocation.height;
+ height = priv->requested_height;
height -= (gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2) +
- padding.top + padding.bottom + border.top + border.bottom;
+ padding.top + padding.bottom;
if (!priv->tearoff_active)
{
GtkWidget *new_child;
gboolean new_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active;
GtkBorder arrow_border;
+
get_arrows_border (menu, &arrow_border);
if (priv->scroll_offset != old_offset)
case GTK_SCROLL_START:
/* Ignore the enter event we might get if the pointer is on the menu */
menu_shell->priv->ignore_enter = TRUE;
- gtk_menu_scroll_to (menu, 0);
gtk_menu_shell_select_first (menu_shell, TRUE);
break;
case GTK_SCROLL_END:
/* Ignore the enter event we might get if the pointer is on the menu */
menu_shell->priv->ignore_enter = TRUE;
- gtk_menu_scroll_to (menu, end_position - page_size);
_gtk_menu_shell_select_last (menu_shell, TRUE);
break;
default:
}
}
-
/**
* gtk_menu_set_monitor:
* @menu: a #GtkMenu
gtk_menu_grab_notify (GtkWidget *widget,
gboolean was_grabbed)
{
+ GtkMenu *menu;
GtkWidget *toplevel;
GtkWindowGroup *group;
GtkWidget *grab;
GdkDevice *pointer;
+ menu = GTK_MENU (widget);
pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (widget));
if (!pointer ||
if (GTK_MENU_SHELL (widget)->priv->active && !GTK_IS_MENU_SHELL (grab))
gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
+
+ menu->priv->drag_scroll_started = FALSE;
}
/**
return !menu->priv->no_toggle_size;
}
+
+/**
+ * gtk_menu_new_from_model:
+ * @model: a #GMenuModel
+ *
+ * Creates a #GtkMenu and populates it with menu items and
+ * submenus according to @model.
+ *
+ * The created menu items are connected to actions found in the
+ * #GtkApplicationWindow to which the menu belongs - typically
+ * by means of being attached to a widget (see gtk_menu_attach_to_widget())
+ * that is contained within the #GtkApplicationWindows widget hierarchy.
+ *
+ * Returns: a new #GtkMenu
+ *
+ * Since: 3.4
+ */
+GtkWidget *
+gtk_menu_new_from_model (GMenuModel *model)
+{
+ GtkWidget *menu;
+
+ g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL);
+
+ menu = gtk_menu_new ();
+ gtk_menu_shell_bind_model (GTK_MENU_SHELL (menu), model, NULL, TRUE);
+
+ return menu;
+}