Refining the concept of focus



To fully implement the XEMBED spec, some refinement of how GTK+ deals with focus
was necessary.

The following patch adds three properties:

 GtkWidget::is_focus - is this focus the focus widget within it's (in-process) GtkWindow
 GtkWindow::is_active - is the (possibly out-of-process) toplevel of the window the X focus
 GtkWIndow::has_toplevel_focus - does this toplevel window contain the focus widget


 +----------- GtkWindow (A)--------------+
 |                                       |
 | GtkPlug (B)------+ GtkPlug-(D)------+ |
 | | GtkEntry (C)-+ | | GtkEntry (E)-+ | |
 | | |            | | | |[focus here]  | | |
 | | +------------+ | | +------------+ | |
 | +----------------+ +----------------+ |
 +---------------------------------------+

So, in a situation like the above, we might have 

 is_focus set on C and E
 is_active set on A, B, and D
 has_toplevel_focus set on A and E 

is_focus is quite useful independent of embedding
is_active is relatively useful independent of embedding ... the most important
  use would be a theme that wanted to draw active widgets differently.
has_toplevel_focus is really an internal implementation detail.

I've made is_active and has_toplevel_focus read-only with private settors,
because

 - The setters just reflect state changes, and have no meaning to an app
 - Making properties read-only will reduce the chance
   of people trying to use them to change the focused window. (And will keep them
   from appearing in GUI-builders.)

GtkWidget::has-focus really should be read-only with GtkWidget::is-focus
being the only read-write property, but making that change at this point
isn't possible.

Regards,
                                        Owen

Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.319
diff -u -p -r1.319 gtkwidget.c
--- gtk/gtkwidget.c	13 May 2002 22:35:42 -0000	1.319
+++ gtk/gtkwidget.c	21 May 2002 21:58:43 -0000
@@ -131,6 +131,7 @@ enum {
   PROP_APP_PAINTABLE,
   PROP_CAN_FOCUS,
   PROP_HAS_FOCUS,
+  PROP_IS_FOCUS,
   PROP_CAN_DEFAULT,
   PROP_HAS_DEFAULT,
   PROP_RECEIVES_DEFAULT,
@@ -454,6 +455,13 @@ gtk_widget_class_init (GtkWidgetClass *k
  							 FALSE,
  							 G_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
+				   PROP_HAS_FOCUS,
+				   g_param_spec_boolean ("is_focus",
+ 							 _("Is focus"),
+ 							 _("Whether the widget is the focus widget within the toplevel"),
+ 							 FALSE,
+ 							 G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
 				   PROP_CAN_DEFAULT,
 				   g_param_spec_boolean ("can_default",
  							 _("Can default"),
@@ -1142,6 +1150,10 @@ gtk_widget_set_property (GObject        
       if (g_value_get_boolean (value))
 	gtk_widget_grab_focus (widget);
       break;
+    case PROP_IS_FOCUS:
+      if (g_value_get_boolean (value))
+	gtk_widget_grab_focus (widget);
+      break;
     case PROP_CAN_DEFAULT:
       saved_flags = GTK_WIDGET_FLAGS (widget);
       if (g_value_get_boolean (value))
@@ -1229,6 +1241,9 @@ gtk_widget_get_property (GObject        
       break;
     case PROP_HAS_FOCUS:
       g_value_set_boolean (value, (GTK_WIDGET_HAS_FOCUS (widget) != FALSE));
+      break;
+    case PROP_IS_FOCUS:
+      g_value_set_boolean (value, (gtk_widget_is_focus (widget)));
       break;
     case PROP_CAN_DEFAULT:
       g_value_set_boolean (value, (GTK_WIDGET_CAN_DEFAULT (widget) != FALSE));
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.208
diff -u -p -r1.208 gtkwindow.c
--- gtk/gtkwindow.c	16 May 2002 23:59:23 -0000	1.208
+++ gtk/gtkwindow.c	21 May 2002 21:58:43 -0000
@@ -70,6 +70,10 @@ enum {
   PROP_DESTROY_WITH_PARENT,
   PROP_ICON,
   PROP_SCREEN,
+
+  /* Readonly properties */
+  PROP_IS_ACTIVE,
+  PROP_HAS_TOPLEVEL_FOCUS,
   
   LAST_ARG
 };
@@ -504,6 +508,22 @@ gtk_window_class_init (GtkWindowClass *k
 							GDK_TYPE_SCREEN,
  							G_PARAM_READWRITE));
 
+  g_object_class_install_property (gobject_class,
+                                   PROP_IS_ACTIVE,
+                                   g_param_spec_boolean ("is_active",
+							 _("Is Active"),
+							 _("Whether the toplevel is the current active window"),
+							 FALSE,
+							 G_PARAM_READABLE));
+  
+  g_object_class_install_property (gobject_class,
+                                   PROP_HAS_TOPLEVEL_FOCUS,
+                                   g_param_spec_boolean ("has_toplevel_focus",
+							 _("Focus in Toplevel"),
+							 _("Whether the input focus is within this GtkWindow"),
+							 FALSE,
+							 G_PARAM_READABLE));
+  
   window_signals[SET_FOCUS] =
     g_signal_new ("set_focus",
                   G_TYPE_FROM_CLASS (object_class),
@@ -770,6 +790,12 @@ gtk_window_get_property (GObject      *o
     case PROP_SCREEN:
       g_value_set_object (value, window->screen);
       break;
+    case PROP_IS_ACTIVE:
+      g_value_set_boolean (value, window->is_active);
+      break;
+    case PROP_HAS_TOPLEVEL_FOCUS:
+      g_value_set_boolean (value, window->has_toplevel_focus);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -3766,14 +3792,10 @@ gtk_window_focus_in_event (GtkWidget    
    */
   if (GTK_WIDGET_VISIBLE (widget))
     {
-      window->has_focus = TRUE;
-      
-      if (window->focus_widget &&
-	  window->focus_widget != widget &&
-	  !GTK_WIDGET_HAS_FOCUS (window->focus_widget))
-	do_focus_change (window->focus_widget, TRUE);	
+      _gtk_window_set_has_toplevel_focus (window, TRUE);
+      _gtk_window_set_is_active (window, TRUE);
     }
-
+      
   return FALSE;
 }
 
@@ -3783,12 +3805,8 @@ gtk_window_focus_out_event (GtkWidget   
 {
   GtkWindow *window = GTK_WINDOW (widget);
 
-  window->has_focus = FALSE;
-  
-  if (window->focus_widget &&
-      window->focus_widget != widget &&
-      GTK_WIDGET_HAS_FOCUS (window->focus_widget))
-    do_focus_change (window->focus_widget, FALSE);
+  _gtk_window_set_has_toplevel_focus (window, FALSE);
+  _gtk_window_set_is_active (window, FALSE);
 
   return FALSE;
 }
@@ -3928,6 +3946,8 @@ gtk_window_real_set_focus (GtkWindow *wi
 
       if (window->has_focus)
 	do_focus_change (window->focus_widget, FALSE);
+
+      g_object_notify (G_OBJECT (window->focus_widget), "is_focus");
     }
   
   window->focus_widget = focus;
@@ -3946,6 +3966,8 @@ gtk_window_real_set_focus (GtkWindow *wi
 
       if (window->has_focus)
 	do_focus_change (window->focus_widget, TRUE);
+
+      g_object_notify (G_OBJECT (window->focus_widget), "is_focus");
     }
   
   if (window->default_widget &&
@@ -6077,4 +6099,82 @@ _gtk_window_activate_key (GtkWindow   *w
     }
   else
     return FALSE;
+}
+
+static void
+window_update_has_focus (GtkWindow *window)
+{
+  GtkWidget *widget = GTK_WIDGET (window);
+  gboolean has_focus = window->has_toplevel_focus && window->is_active;
+  
+  if (has_focus != window->has_focus)
+    {
+      window->has_focus = has_focus;
+      
+      if (has_focus)
+	{
+	  if (window->focus_widget &&
+	      window->focus_widget != widget &&
+	      !GTK_WIDGET_HAS_FOCUS (window->focus_widget))
+	    do_focus_change (window->focus_widget, TRUE);	
+	}
+      else
+	{
+	  if (window->focus_widget &&
+	      window->focus_widget != widget &&
+	      GTK_WIDGET_HAS_FOCUS (window->focus_widget))
+	    do_focus_change (window->focus_widget, FALSE);
+	}
+    }
+}
+
+/**
+ * _gtk_window_set_is_active:
+ * @window: a #GtkWindow
+ * @is_active: %TRUE if the window is in the currently active toplevel
+ * 
+ * Internal function that sets whether the #GtkWindow is part
+ * of the currently active toplevel window (taking into account inter-process
+ * embedding.)
+ **/
+void
+_gtk_window_set_is_active (GtkWindow *window,
+			   gboolean   is_active)
+{
+  g_return_if_fail (GTK_IS_WINDOW (window));
+
+  is_active = is_active != FALSE;
+
+  if (is_active != window->is_active)
+    {
+      window->is_active = is_active;
+      window_update_has_focus (window);
+
+      g_object_notify (G_OBJECT (window), "is_active");
+    }
+}
+
+/**
+ * _gtk_window_set_has_toplevel_focus:
+ * @window: a #GtkWindow
+ * @has_toplevel_focus: %TRUE if the in
+ * 
+ * Internal function that sets whether the keyboard focus for the
+ * toplevel window (taking into account inter-process embedding.)
+ **/
+void
+_gtk_window_set_has_toplevel_focus (GtkWindow *window,
+				   gboolean   has_toplevel_focus)
+{
+  g_return_if_fail (GTK_IS_WINDOW (window));
+  
+  has_toplevel_focus = has_toplevel_focus != FALSE;
+
+  if (has_toplevel_focus != window->has_toplevel_focus)
+    {
+      window->has_toplevel_focus = has_toplevel_focus;
+      window_update_has_focus (window);
+
+      g_object_notify (G_OBJECT (window), "has_toplevel_focus");
+    }
 }
Index: gtk/gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.62
diff -u -p -r1.62 gtkwindow.h
--- gtk/gtkwindow.h	29 Apr 2002 22:53:45 -0000	1.62
+++ gtk/gtkwindow.h	21 May 2002 21:58:43 -0000
@@ -98,7 +98,10 @@ struct _GtkWindow
   guint decorated : 1;
   
   guint type_hint : 3; /* GdkWindowTypeHint */ 
-  guint gravity : 5; /* GdkGravity */ 
+  guint gravity : 5; /* GdkGravity */
+
+  guint is_active : 1;
+  guint has_toplevel_focus : 1;
   
   guint frame_left;
   guint frame_top;
@@ -343,6 +346,11 @@ void            _gtk_window_constrain_si
 GtkWindowGroup *_gtk_window_get_group          (GtkWindow *window);
 gboolean        _gtk_window_activate_key       (GtkWindow   *window,
 						GdkEventKey *event);
+
+void            _gtk_window_set_has_toplevel_focus (GtkWindow *window,
+						    gboolean   has_toplevel_focus);
+void            _gtk_window_set_is_active          (GtkWindow *window,
+						    gboolean   is_active);
 
 typedef void (*GtkWindowKeysForeachFunc) (GtkWindow      *window,
 					  guint           keyval,


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