Preliminary submenu patch



Attached find a preliminary patch to improve submenu navigation, a la
Mac.

It deals properly with mouse movement and with keystrokes:
when the pointer leaves a menu item, it creates a GdkRegion (the
triangular region that I've been going on about), and then every
menu_motion event and menu_shell enter/leave event checks the pointer
to see that it's in the region. If so, the event is ignored. If not
(or if the pointer is NULL) it kills the region and the event is
procesed as usual.
Any keyboard action kills the region.
The region is stored as a static pointer in gtkmenushell.c

Note that testgtk was very helpful in bug hunting and in code design:
it showed bugs in early attempts I had never thought of.

Questions:
(1) What's the proper way of dereferencing a GdkRegion?
(2) I haven't implemented a timeout -- I was thinking of copying the
    code from gtk_real_menu_item_select -- is this right? (David?)
(3) Currently, it's ugly in that to share a function between
    gtkmenushell and gtkmenu, I make public functions available.
    Would an acceptable better way be to add a line like:

if (GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event))
  return TRUE;

    to gtkmenu.c:gtk_menu_motion_notify_event ()
    and then make gtk_menu_shell_motion_notify_event () which just
    called
if (gtk_menu_shell_navigating ((gint) event->x_root, (gint)
    event->y_root))
return TRUE;
     ? (I didn't do this b/c it involved adding new signal handlers, and I wanted
     to make sure this was kosher).

I'll be graduating tomorrow, so my computer will be packed up and I'll
have difficulty replying promptly. This will hopefully be remedied
next week and I'll be able to resume work/fix the above concerns.

Sorry for taking so long with this.

-- 
  -nils
Public key: http://www.fas.harvard.edu/~nbarth/pub-key.txt
Index: gtkmenu.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenu.c,v
retrieving revision 1.45
diff -u -r1.45 gtkmenu.c
--- gtkmenu.c	2000/05/21 06:13:34	1.45
+++ gtkmenu.c	2000/06/07 13:05:32
@@ -30,11 +30,11 @@
 #include "gtklabel.h"
 #include "gtkmain.h"
 #include "gtkmenu.h"
+#include "gtkmenushell.h"
 #include "gtkmenuitem.h"
 #include "gtksignal.h"
 #include "gtkwindow.h"
 
-
 #define MENU_ITEM_CLASS(w)   GTK_MENU_ITEM_GET_CLASS (w)
 #define	MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
 
@@ -1103,14 +1103,24 @@
 }
 
 static gint 
-gtk_menu_motion_notify  (GtkWidget	   *widget,
-			 GdkEventMotion    *event)
+gtk_menu_motion_notify (GtkWidget	  *widget,
+			GdkEventMotion    *event)
 {
+  GtkMenu *menu;
+  GtkMenuShell *menu_shell;
+  
   g_return_val_if_fail (widget != NULL, FALSE);
   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
   
-  if (GTK_MENU_SHELL (widget)->ignore_enter)
-    GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
+  menu = GTK_MENU (widget);
+
+/* start */
+  if (gtk_menu_shell_navigating ((gint) event->x_root, (gint) event->y_root))
+    return TRUE;
+  menu_shell = GTK_MENU_SHELL (widget);
+/* end */
+  if (menu_shell->ignore_enter)
+    menu_shell->ignore_enter = FALSE;
   else 
     {
       gint width, height;
@@ -1125,11 +1135,10 @@
 	  send_event.crossing.window = event->window;
 	  send_event.crossing.time = event->time;
 	  send_event.crossing.send_event = TRUE;
-	  
+
 	  gtk_widget_event (widget, &send_event);
 	}
     }
-
   return FALSE;
 }
 
Index: gtkmenushell.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.c,v
retrieving revision 1.32
diff -u -r1.32 gtkmenushell.c
--- gtkmenushell.c	2000/05/12 15:25:45	1.32
+++ gtkmenushell.c	2000/06/07 13:05:33
@@ -32,7 +32,6 @@
 #include "gtkmenushell.h"
 #include "gtksignal.h"
 
-
 #define MENU_SHELL_TIMEOUT   500
 
 
@@ -88,7 +87,7 @@
  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
  *          in this menu.
  * 
- *     As a a bit of a hack to get movement between menus and
+ *     As a bit of a hack to get movement between menus and
  *     menubars working, if submenu_placement is different for
  *     the menu and its MenuShell then the following apply:
  * 
@@ -121,6 +120,8 @@
 					      GdkEventCrossing  *event);
 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
 					      GdkEventCrossing  *event);
+static void gtk_menu_shell_set_navigation_region (GtkMenuItem   *menu_item,
+                                              GdkEventCrossing  *event);
 static void gtk_menu_shell_add               (GtkContainer      *container,
 					      GtkWidget         *widget);
 static void gtk_menu_shell_remove            (GtkContainer      *container,
@@ -145,6 +146,7 @@
 
 static GtkContainerClass *parent_class = NULL;
 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
+static GdkRegion *navigation_region = NULL;
 
 
 GtkType
@@ -539,7 +541,9 @@
   g_return_val_if_fail (event != NULL, FALSE);
       
   menu_shell = GTK_MENU_SHELL (widget);
-
+  
+  gtk_menu_shell_stop_navigating ();
+  
   if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
   
@@ -559,7 +563,7 @@
 			     GdkEventCrossing *event)
 {
   GtkMenuShell *menu_shell;
-  GtkWidget *menu_item;
+  GtkWidget *event_widget;
 
   g_return_val_if_fail (widget != NULL, FALSE);
   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
@@ -569,19 +573,23 @@
 
   if (menu_shell->active && !menu_shell->ignore_enter)
     {
-      menu_item = gtk_get_event_widget ((GdkEvent*) event);
+      event_widget = gtk_get_event_widget ((GdkEvent*) event);
 
-      if (!menu_item || !GTK_WIDGET_IS_SENSITIVE (menu_item))
+      if (!event_widget || !GTK_WIDGET_IS_SENSITIVE (event_widget))
 	return TRUE;
-
-      if ((menu_item->parent == widget) &&
-	  (menu_shell->active_menu_item != menu_item) &&
-	  GTK_IS_MENU_ITEM (menu_item))
+/* start */
+      if (gtk_menu_shell_navigating ((gint) event->x_root,
+	    (gint) event->y_root))
+	return TRUE; /* Don't do anything -- we're navigating */
+/* end */
+      if ((event_widget->parent == widget) &&
+	  (menu_shell->active_menu_item != event_widget) &&
+	  GTK_IS_MENU_ITEM (event_widget))
 	{
 	  if ((event->detail != GDK_NOTIFY_INFERIOR) &&
-	      (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
+	      (GTK_WIDGET_STATE (event_widget) != GTK_STATE_PRELIGHT))
 	    {
-	      gtk_menu_shell_select_item (menu_shell, menu_item);
+	      gtk_menu_shell_select_item (menu_shell, event_widget);
 	    }
 	}
       else if (menu_shell->parent_menu_shell)
@@ -623,6 +631,24 @@
 
       if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
 	return TRUE;
+/* start */
+      if (gtk_menu_shell_navigating ((gint) event->x_root,
+	    (gint) event->y_root))
+	return TRUE; /* Don't do anything -- we're navigating */
+      /* Here we check to see if we're leaving an active submenu,
+       * in which case we enter submenu navigation mode.
+       */
+      if (menu_shell->active_menu_item != NULL
+	  && menu_shell->active_menu_item != event_widget
+	  /*&& menu_item == gtk_menu_nav_get_current_item (menu_shell)*/
+          && menu_item->submenu != NULL
+	  && menu_item->submenu_placement == GTK_LEFT_RIGHT)
+	if (menu_item->submenu->window != NULL)
+	  {
+	    gtk_menu_shell_set_navigation_region (menu_item, event);
+	    return TRUE;
+	  }
+/* end */
 
       if ((menu_shell->active_menu_item == event_widget) &&
 	  (menu_item->submenu == NULL))
@@ -640,6 +666,79 @@
     }
 
   return TRUE;
+}
+
+void
+gtk_menu_shell_stop_navigating ()
+{
+  /* FIXME: Should I deallocate the region? */
+  navigation_region = NULL;
+}
+
+gboolean
+gtk_menu_shell_navigating (gint event_x, gint event_y)
+{
+  if (navigation_region)
+  {
+    if (gdk_region_point_in (navigation_region, event_x, event_y))
+      return TRUE;
+    else
+    {
+      gtk_menu_shell_stop_navigating ();
+      return FALSE;
+    }
+  }
+  return FALSE;
+}
+
+static void
+gtk_menu_shell_set_navigation_region (GtkMenuItem      *menu_item,
+				      GdkEventCrossing *event)
+{
+  gint submenu_left = 0, submenu_right  = 0;
+  gint submenu_top  = 0, submenu_bottom = 0;
+  gint width        = 0, height         = 0;
+  GdkPoint point[3];
+  GtkWidget *event_widget;
+
+  g_return_if_fail (menu_item->submenu != NULL);
+  g_return_if_fail (event != NULL);
+  
+  event_widget = gtk_get_event_widget ((GdkEvent*) event);
+  
+  gdk_window_get_root_origin (menu_item->submenu->window,
+      &submenu_left, &submenu_top);
+  gdk_window_get_size (menu_item->submenu->window, &width, &height);
+  submenu_right = submenu_left + width;
+  submenu_bottom = submenu_top + height;
+  gdk_window_get_size (event_widget->window, &width, &height);
+  /* FIXME: 6 is a magic number -- why does it work? */
+  if ((event->x >= 0) && (event->x <= width - 6)
+      && !((event->y < 0) && (event->y_root <= submenu_top))
+      && !((event->y >= 0) && (event->y_root >= submenu_bottom)))
+  {
+    /* Set navigation region */
+    point[0].x = (gint16) event->x_root;
+    point[0].y = (gint16) event->y_root;
+    if (event->y < 0) /* Exiting the top */
+      point[1].y = (gint16) submenu_top;
+    else /* Exiting the bottom */
+      point[1].y = (gint16) submenu_bottom;
+    if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
+      point[1].x = (gint16) submenu_left; /* submenu is on the right */
+    else /* submenu is on the left */
+      point[1].x = (gint16) submenu_right;
+    point[2].x = point[1].x;
+    point[2].y = point[0].y;
+/*    g_print ("menushell: event->(x,y): (%d,%d)\n", (gint) event->x,
+	(gint) event->y);
+    g_print ("(%d,%d) (%d,%d) (%d,%d)\n",
+	point[0].x, point[0].y,
+	point[1].x, point[1].y,
+	point[2].x, point[2].y); */
+    
+    navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
+  }
 }
 
 static void
Index: gtkmenushell.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.h,v
retrieving revision 1.8
diff -u -r1.8 gtkmenushell.h
--- gtkmenushell.h	2000/02/13 08:16:47	1.8
+++ gtkmenushell.h	2000/06/07 13:05:33
@@ -55,7 +55,8 @@
   GList *children;
   GtkWidget *active_menu_item;
   GtkWidget *parent_menu_shell;
-  
+
+/*  GdkRegion *navigation_region; */
   guint active : 1;
   guint have_grab : 1;
   guint have_xgrab : 1;
@@ -99,8 +100,9 @@
 void    gtk_menu_shell_activate_item  (GtkMenuShell      *menu_shell,
 				       GtkWidget         *menu_item,
 				       gboolean           force_deactivate);
-
-
+void    gtk_menu_shell_stop_navigating ();
+gboolean gtk_menu_shell_navigating        (gint              event_x,
+					   gint              event_y);
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]