[gtk+/wip/matthiasc/password-indicator: 2/2] entry: Add a password indicator



commit 2313f5ac5c6c654c137bb0bab53981424d528b28
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Dec 18 14:21:10 2015 -0500

    entry: Add a password indicator
    
    This adds a password-indicator property to GtkEntry. When set
    on a password entry, the entry shows an icon indicating it is
    in password mode, which can be clicked to reveal the text.
    
    We are careful to not interfere with the Caps Lock warning,
    which uses the same icon position.

 gtk/gtkentry.c                                |  134 +++++++++++++++++++++++--
 gtk/icons/16x16/status/password-invisible.png |  Bin 0 -> 284 bytes
 gtk/icons/16x16/status/password-visible.png   |  Bin 0 -> 312 bytes
 tests/testentryicons.c                        |    3 +
 4 files changed, 129 insertions(+), 8 deletions(-)
---
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index 6318af7..ad83f6b 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -243,6 +243,8 @@ struct _GtkEntryPrivate
   guint         cache_includes_preedit  : 1;
   guint         caps_lock_warning       : 1;
   guint         caps_lock_warning_shown : 1;
+  guint         password_indicator      : 1;
+  guint         password_indicator_connected : 1;
   guint         change_count            : 8;
   guint         cursor_visible          : 1;
   guint         editing_canceled        : 1; /* Only used by GtkCellRendererText */
@@ -333,6 +335,7 @@ enum {
   PROP_TEXT_LENGTH,
   PROP_INVISIBLE_CHAR_SET,
   PROP_CAPS_LOCK_WARNING,
+  PROP_PASSWORD_INDICATOR,
   PROP_PROGRESS_FRACTION,
   PROP_PROGRESS_PULSE_STEP,
   PROP_PIXBUF_PRIMARY,
@@ -649,6 +652,7 @@ static void         get_frame_size                     (GtkEntry       *entry,
 static void         gtk_entry_move_adjustments         (GtkEntry             *entry);
 static void         gtk_entry_update_cached_style_values(GtkEntry      *entry);
 static gboolean     get_middle_click_paste             (GtkEntry *entry);
+static void         update_password_indicator          (GtkEntry       *entry);
 
 /* GtkTextHandle handlers */
 static void         gtk_entry_handle_drag_started      (GtkTextHandle         *handle,
@@ -1031,7 +1035,7 @@ gtk_entry_class_init (GtkEntryClass *class)
   /**
    * GtkEntry:caps-lock-warning:
    *
-   * Whether password entries will show a warning when Caps Lock is on.
+   * Whether the entry will show a warning when Caps Lock is on in password mode.
    *
    * Note that the warning is shown using a secondary icon, and thus
    * does not work if you are using the secondary icon position for some
@@ -1042,11 +1046,29 @@ gtk_entry_class_init (GtkEntryClass *class)
   entry_props[PROP_CAPS_LOCK_WARNING] =
       g_param_spec_boolean ("caps-lock-warning",
                             P_("Caps Lock warning"),
-                            P_("Whether password entries will show a warning when Caps Lock is on"),
+                            P_("Whether the entry shows a warning when Caps Lock is on in password mode"),
                             TRUE,
                             GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
 
   /**
+   * GtkEntry:password-indicator:
+   *
+   * Whether the entry shows an icon to toggle password mode on and off.
+   *
+   * Note that the icon is shown using a secondary icon, and thus
+   * does not work if you are using the secondary icon position for some
+   * other purpose.
+   *
+   * Since: 3.20
+   */
+  entry_props[PROP_PASSWORD_INDICATOR] =
+      g_param_spec_boolean ("password-indicator",
+                            P_("Password indicator"),
+                            P_("Whether the entry shows an icon to toggle password mode"),
+                            FALSE,
+                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+  /**
    * GtkEntry:progress-fraction:
    *
    * The current fraction of the task that's been completed.
@@ -2226,6 +2248,15 @@ gtk_entry_set_property (GObject         *object,
         }
       break;
 
+    case PROP_PASSWORD_INDICATOR:
+      if (priv->password_indicator != g_value_get_boolean (value))
+        {
+          priv->password_indicator = g_value_get_boolean (value);
+          update_password_indicator (entry);
+          g_object_notify_by_pspec (object, pspec);
+        }
+      break;
+
     case PROP_PROGRESS_FRACTION:
       gtk_entry_set_progress_fraction (entry, g_value_get_double (value));
       break;
@@ -2489,6 +2520,10 @@ gtk_entry_get_property (GObject         *object,
       g_value_set_boolean (value, priv->caps_lock_warning);
       break;
 
+    case PROP_PASSWORD_INDICATOR:
+      g_value_set_boolean (value, priv->password_indicator);
+      break;
+
     case PROP_PROGRESS_FRACTION:
       g_value_set_double (value, priv->progress_fraction);
       break;
@@ -2711,6 +2746,8 @@ gtk_entry_init (GtkEntry *entry)
   priv->xalign = 0.0;
   priv->caps_lock_warning = TRUE;
   priv->caps_lock_warning_shown = FALSE;
+  priv->password_indicator = FALSE;
+  priv->password_indicator_connected = FALSE;
   priv->progress_fraction = 0.0;
   priv->progress_pulse_fraction = 0.1;
 
@@ -3144,7 +3181,7 @@ realize_icon_info (GtkWidget            *widget,
                                 GDK_BUTTON3_MOTION_MASK |
                                 GDK_POINTER_MOTION_MASK |
                                 GDK_ENTER_NOTIFY_MASK |
-                            GDK_LEAVE_NOTIFY_MASK);
+                                GDK_LEAVE_NOTIFY_MASK);
   attributes_mask = GDK_WA_X | GDK_WA_Y;
 
   icon_info->window = gdk_window_new (gtk_widget_get_window (widget),
@@ -3402,6 +3439,7 @@ gtk_entry_realize (GtkWidget *widget)
 
   gtk_entry_adjust_scroll (entry);
   gtk_entry_update_primary_selection (entry);
+  update_password_indicator (entry);
 
   /* If the icon positions are already setup, create their windows.
    * Otherwise if they don't exist yet, then construct_icon_info()
@@ -4325,6 +4363,8 @@ gtk_entry_event (GtkWidget *widget,
         }
     }
 
+g_print ("entry event: %s, icon_info %p\n", (event->type == GDK_BUTTON_PRESS ? "press" : (event->type == 
GDK_BUTTON_RELEASE ? "release" : (event->type == GDK_MOTION_NOTIFY ? "motion" : "other"))), icon_info);
+
   if (!icon_info)
     return GDK_EVENT_PROPAGATE;
 
@@ -4346,6 +4386,7 @@ gtk_entry_event (GtkWidget *widget,
     case GDK_BUTTON_PRESS:
     case GDK_2BUTTON_PRESS:
     case GDK_3BUTTON_PRESS:
+g_print ("button press\n");
       if (should_prelight (GTK_ENTRY (widget), i))
         {
           icon_info->prelight = FALSE;
@@ -4359,8 +4400,10 @@ gtk_entry_event (GtkWidget *widget,
       icon_info->device = device;
 
       if (!icon_info->nonactivatable)
+{
+g_print ("::icon-press %d\n", i);
         g_signal_emit (widget, signals[ICON_PRESS], 0, i, event);
-
+}
       break;
     case GDK_TOUCH_UPDATE:
       if (icon_info->device != device ||
@@ -4394,6 +4437,7 @@ gtk_entry_event (GtkWidget *widget,
       icon_info->current_sequence = NULL;
       /* Fall through */
     case GDK_BUTTON_RELEASE:
+g_print ("button release\n");
       icon_info->pressed = FALSE;
       icon_info->device = NULL;
 
@@ -4408,8 +4452,10 @@ gtk_entry_event (GtkWidget *widget,
         }
 
       if (!icon_info->nonactivatable)
+{
+g_print ("::icon-release %d\n", i);
         g_signal_emit (widget, signals[ICON_RELEASE], 0, i, event);
-
+}
       break;
     default:
       return GDK_EVENT_PROPAGATE;
@@ -7814,6 +7860,7 @@ gtk_entry_set_visibility (GtkEntry *entry,
     {
       priv->visible = visible;
 
+      update_password_indicator (entry);
       g_object_notify_by_pspec (G_OBJECT (entry), entry_props[PROP_VISIBILITY]);
       gtk_entry_recompute (entry);
     }
@@ -10988,7 +11035,77 @@ gtk_entry_get_placeholder_text (GtkEntry *entry)
   return priv->placeholder_text;
 }
 
-/* Caps Lock warning for password entries */
+/* Caps Lock warning and password indicator */
+
+static void
+password_indicator_show (GtkEntry             *entry,
+                         GtkEntryIconPosition  pos,
+                         GdkEvent             *event,
+                         gpointer              data)
+{
+  GtkEntryPrivate *priv = entry->priv;
+
+  if (pos == GTK_ENTRY_ICON_SECONDARY && priv->password_indicator)
+    gtk_entry_set_visibility (entry, TRUE);
+}
+
+static void
+password_indicator_hide (GtkEntry             *entry,
+                         GtkEntryIconPosition  pos,
+                         GdkEvent             *event,
+                         gpointer              data)
+{
+  GtkEntryPrivate *priv = entry->priv;
+
+  if (pos == GTK_ENTRY_ICON_SECONDARY && priv->password_indicator)
+    gtk_entry_set_visibility (entry, FALSE);
+}
+
+static void
+update_password_indicator (GtkEntry *entry)
+{
+  GtkEntryPrivate *priv = entry->priv;
+
+  if (priv->caps_lock_warning_shown)
+    return;
+
+  if (priv->password_indicator)
+    {
+      if (!priv->password_indicator_connected)
+        {
+          g_signal_connect (entry, "icon-press", G_CALLBACK (password_indicator_show), NULL);
+          g_signal_connect (entry, "icon-release", G_CALLBACK (password_indicator_hide), NULL);
+          priv->password_indicator_connected = TRUE;
+        }
+
+      gtk_entry_set_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY, TRUE);
+
+      if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL)
+        {
+          gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, "password-invisible");
+          gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, _("Show text"));
+        }
+      else
+        {
+          gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, "password-visible");
+          gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, _("Hide text"));
+        }
+
+    }
+  else
+    {
+      if (priv->password_indicator_connected)
+        {
+          g_signal_handlers_disconnect_by_func (entry, password_indicator_show, NULL);
+          g_signal_handlers_disconnect_by_func (entry, password_indicator_hide, NULL);
+          priv->password_indicator_connected = FALSE;
+        }
+
+      gtk_entry_set_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
+      gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+      gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
+    }
+}
 
 static void
 show_capslock_feedback (GtkEntry    *entry,
@@ -10996,7 +11113,8 @@ show_capslock_feedback (GtkEntry    *entry,
 {
   GtkEntryPrivate *priv = entry->priv;
 
-  if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_SECONDARY) == GTK_IMAGE_EMPTY)
+  if (gtk_entry_get_icon_storage_type (entry, GTK_ENTRY_ICON_SECONDARY) == GTK_IMAGE_EMPTY ||
+      priv->password_indicator)
     {
       gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, "dialog-warning-symbolic");
       gtk_entry_set_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
@@ -11016,8 +11134,8 @@ remove_capslock_feedback (GtkEntry *entry)
 
   if (priv->caps_lock_warning_shown)
     {
-      gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
       priv->caps_lock_warning_shown = FALSE;
+      update_password_indicator (entry);
     } 
 }
 
diff --git a/gtk/icons/16x16/status/password-invisible.png b/gtk/icons/16x16/status/password-invisible.png
new file mode 100644
index 0000000..54eeba9
Binary files /dev/null and b/gtk/icons/16x16/status/password-invisible.png differ
diff --git a/gtk/icons/16x16/status/password-visible.png b/gtk/icons/16x16/status/password-visible.png
new file mode 100644
index 0000000..fee7fbd
Binary files /dev/null and b/gtk/icons/16x16/status/password-visible.png differ
diff --git a/tests/testentryicons.c b/tests/testentryicons.c
index df1d02b..f0a3c49 100644
--- a/tests/testentryicons.c
+++ b/tests/testentryicons.c
@@ -165,6 +165,8 @@ main (int argc, char **argv)
   gtk_grid_attach (GTK_GRID (grid), entry, 1, 3, 1, 1);
   gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
 
+  g_object_set (entry, "password-indicator", TRUE, NULL);
+#if 0
   gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
                                      GTK_ENTRY_ICON_PRIMARY,
                                      "dialog-password-symbolic");
@@ -172,6 +174,7 @@ main (int argc, char **argv)
   gtk_entry_set_icon_activatable (GTK_ENTRY (entry),
                                  GTK_ENTRY_ICON_PRIMARY,
                                  FALSE);
+#endif
 
   /* Name - Does not set any icons. */
   label = gtk_label_new ("Name:");


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