Menu blinking animation



Here's a small patch to add menu blinking to GTK+.  It's modified off a
patch Anders did a while ago.  I'd like to get something like it into
GTK+-2.6, but before I do so, I had a couple of questions before
spending more time on it.

 * Is this the effect we want?  I really like having feedback when
   activating a menu item, so we should put something there.  It's not
   super flashy though, perhaps someone can come up with something
   better?

 * There's a small delay between selecting the item and the activation.
   It's not a really long delay (150ms currently), but it is a little
   obnoxious to have to wait for the thing you activate to actually
   happen.

There are also a few minor issues with the patch which I'll look at.
First, it should be made time based like other such effects.  This is
less important than other animations as it's so short, but it would
still be nice.  Second, it probably shouldn't delay the activation if
the menu isn't actually popped up, as is the case when the accessibility
framework is on.  Finally, we need a style-property for the duration of
the effect, with the caveats that Matthias brought up at:

http://mail.gnome.org/archives/gtk-devel-list/2004-July/msg00229.html

Thoughts?
-Jonathan

Index: gtk/gtkmenushell.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.c,v
retrieving revision 1.80
diff -u -r1.80 gtkmenushell.c
--- gtk/gtkmenushell.c	9 Aug 2004 16:59:52 -0000	1.80
+++ gtk/gtkmenushell.c	5 Oct 2004 18:45:14 -0000
@@ -25,11 +25,13 @@
  */
 
 #define GTK_MENU_INTERNALS
+#define GTK_MENU_SHELL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_MENU_SHELL, GtkMenuShellPrivate))
 
 #include <config.h>
 #include "gdk/gdkkeysyms.h"
 #include "gtkalias.h"
 #include "gtkbindings.h"
+#include "gtkinvisible.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
 #include "gtkmenubar.h"
@@ -38,7 +40,7 @@
 #include "gtktearoffmenuitem.h"
 #include "gtkwindow.h"
 
-#define MENU_SHELL_TIMEOUT   500
+#define MENU_SHELL_TIMEOUT 500
 
 enum {
   DEACTIVATE,
@@ -50,6 +52,21 @@
   LAST_SIGNAL
 };
 
+typedef struct _GtkMenuShellPrivate GtkMenuShellPrivate;
+struct _GtkMenuShellPrivate
+{
+
+  /* Fields for the blinking  */
+  guint animation_id;
+
+  GtkWidget *menu_item;
+  GtkMenuShell *menu_shell;
+  GtkWidget *invisible;
+  
+  int state;
+  gboolean force_deactivate;
+};
+
 typedef void (*GtkMenuShellSignal1) (GtkObject           *object,
 				     GtkMenuDirectionType arg1,
 				     gpointer             data);
@@ -114,6 +131,7 @@
 
 static void gtk_menu_shell_class_init        (GtkMenuShellClass *klass);
 static void gtk_menu_shell_init              (GtkMenuShell      *menu_shell);
+static void gtk_menu_shell_destroy           (GtkObject         *object);
 static void gtk_menu_shell_realize           (GtkWidget         *widget);
 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
 					      GdkEventButton    *event);
@@ -190,18 +208,22 @@
 static void
 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
 {
-  GObjectClass *object_class;
+  GObjectClass *gobject_class;
+  GtkObjectClass *object_class;
   GtkWidgetClass *widget_class;
   GtkContainerClass *container_class;
 
   GtkBindingSet *binding_set;
 
-  object_class = (GObjectClass*) klass;
+  gobject_class = (GObjectClass*) klass;
+  object_class = (GtkObjectClass*) klass;
   widget_class = (GtkWidgetClass*) klass;
   container_class = (GtkContainerClass*) klass;
 
   parent_class = g_type_class_peek_parent (klass);
 
+  object_class->destroy = gtk_menu_shell_destroy;
+
   widget_class->realize = gtk_menu_shell_realize;
   widget_class->button_press_event = gtk_menu_shell_button_press;
   widget_class->button_release_event = gtk_menu_shell_button_release;
@@ -233,7 +255,7 @@
 		  G_TYPE_NONE, 0);
   menu_shell_signals[SELECTION_DONE] =
     g_signal_new ("selection-done",
-		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_OBJECT_CLASS_TYPE (gobject_class),
 		  G_SIGNAL_RUN_FIRST,
 		  G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
 		  NULL, NULL,
@@ -241,7 +263,7 @@
 		  G_TYPE_NONE, 0);
   menu_shell_signals[MOVE_CURRENT] =
     g_signal_new ("move_current",
-		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_OBJECT_CLASS_TYPE (gobject_class),
 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 		  G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
 		  NULL, NULL,
@@ -250,7 +272,7 @@
 		  GTK_TYPE_MENU_DIRECTION_TYPE);
   menu_shell_signals[ACTIVATE_CURRENT] =
     g_signal_new ("activate_current",
-		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_OBJECT_CLASS_TYPE (gobject_class),
 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 		  G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
 		  NULL, NULL,
@@ -259,7 +281,7 @@
 		  G_TYPE_BOOLEAN);
   menu_shell_signals[CANCEL] =
     g_signal_new ("cancel",
-		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_OBJECT_CLASS_TYPE (gobject_class),
 		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 		  G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
 		  NULL, NULL,
@@ -267,7 +289,7 @@
 		  G_TYPE_NONE, 0);
   menu_shell_signals[CYCLE_FOCUS] =
     _gtk_binding_signal_new ("cycle_focus",
-			     G_OBJECT_CLASS_TYPE (object_class),
+			     G_OBJECT_CLASS_TYPE (gobject_class),
 			     G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 			     G_CALLBACK (gtk_real_menu_shell_cycle_focus),
 			     NULL, NULL,
@@ -308,6 +330,8 @@
 				GDK_F10, GDK_SHIFT_MASK,
 				"cycle_focus", 1,
                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
+
+  g_type_class_add_private (gobject_class, sizeof (GtkMenuShellPrivate));
 }
 
 static GType
@@ -378,6 +402,32 @@
 }
 
 static void
+gtk_menu_shell_destroy (GtkObject *object)
+{
+  GtkMenuShellPrivate *private;
+
+  private = GTK_MENU_SHELL_GET_PRIVATE (object);
+
+  if (private->animation_id > 0)
+    {
+      g_source_remove (private->animation_id);
+      private->animation_id = 0;
+    }
+  if (private->invisible)
+    {
+      gtk_widget_destroy (private->invisible);
+      private->invisible = NULL;
+    }
+  if (private->menu_item)
+    {
+      g_object_unref (private->menu_item);
+      private->menu_item = NULL;
+    }
+
+  GTK_OBJECT_CLASS (parent_class)->destroy (object);
+}
+
+static void
 gtk_menu_shell_realize (GtkWidget *widget)
 {
   GdkWindowAttr attributes;
@@ -852,9 +902,11 @@
       menu_shell->active_menu_item = NULL;
     }
 }
+#define NUMBER_OF_ANIMATION_BLINKS 1
+#define ANIMATION_INTERVAL 75
 
 void
-gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
+activate_item (GtkMenuShell      *menu_shell,
 			      GtkWidget         *menu_item,
 			      gboolean           force_deactivate)
 {
@@ -900,6 +952,79 @@
   g_slist_free (shells);
 
   g_object_unref (menu_shell);
+}
+
+static gboolean
+gtk_menu_shell_animation_timeout (gpointer data)
+{
+  GtkMenuShell *menu_shell;
+  GtkMenuShellPrivate *private;
+  gboolean retval = TRUE;
+
+  GDK_THREADS_ENTER ();
+
+  menu_shell = GTK_MENU_SHELL (data);
+  private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+  private->state++;
+
+  gtk_widget_set_state (private->menu_item, private->state % 2 == 0 ?
+			GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
+
+  gtk_widget_queue_draw (private->menu_item);
+  
+  if (private->state > NUMBER_OF_ANIMATION_BLINKS * 2 + 1)
+    {
+      /* Exit the animation */
+      activate_item (menu_shell, private->menu_item, private->force_deactivate);
+      private->animation_id = 0;
+      gtk_grab_remove (private->invisible);
+      g_object_unref (private->menu_item);
+      private->menu_item = NULL;
+      retval = FALSE;
+    }
+
+  GDK_THREADS_LEAVE ();
+
+  return retval;
+}
+
+static void
+gtk_menu_shell_schedule_animation (GtkMenuShell *menu_shell, GtkWidget *menu_item, gboolean force_deactivate)
+{
+  GtkMenuShellPrivate *private;
+
+  private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+  /* There can only be one animation at a time */
+  if (private->animation_id > 0)
+    g_source_remove (private->animation_id);
+
+  /* Prep for the animation */
+  private->state = 0;
+  if (private->invisible == NULL)
+    private->invisible = gtk_invisible_new ();
+  gtk_grab_add (private->invisible);
+  private->force_deactivate = force_deactivate;
+
+  /* Add a ref to the menu_item incase something weird happens in the
+   * middle of the animation. */
+  private->menu_item = g_object_ref (menu_item);
+
+  GTK_MENU_SHELL_GET_PRIVATE (menu_shell)->animation_id =
+    g_timeout_add (ANIMATION_INTERVAL, gtk_menu_shell_animation_timeout, menu_shell);
+}
+
+void
+gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
+			      GtkWidget         *menu_item,
+			      gboolean           force_deactivate)
+{
+
+  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+  gtk_menu_shell_schedule_animation (menu_shell, menu_item, force_deactivate);
 }
 
 /* Distance should be +/- 1 */


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