[gtk/peek-password: 6/11] password entry: Add a way to see the content
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/peek-password: 6/11] password entry: Add a way to see the content
- Date: Thu, 14 Mar 2019 02:29:03 +0000 (UTC)
commit 1b504082e58787c659ee5b4d2b895919bb906028
Author: Matthias Clasen <mclasen redhat com>
Date: Wed Mar 13 16:27:07 2019 -0400
password entry: Add a way to see the content
Add a ::show-peek-icon property and show a clickable
icon when it is set. Clicking it toggles the visibility
of the content. The same functionality is also accessible
via a context menu item.
This is a common feature of password entries.
gtk/gtkpasswordentry.c | 131 ++++++++++++++++++++-
gtk/gtkpasswordentry.h | 6 +
.../password-invisible-symbolic.symbolic.png | Bin 0 -> 168 bytes
.../status/password-visible-symbolic.symbolic.png | Bin 0 -> 177 bytes
.../status/password-invisible-symbolic.svg | 7 ++
.../scalable/status/password-visible-symbolic.svg | 7 ++
tests/testentryicons.c | 1 +
7 files changed, 149 insertions(+), 3 deletions(-)
---
diff --git a/gtk/gtkpasswordentry.c b/gtk/gtkpasswordentry.c
index 847056eecf..ada47e39fe 100644
--- a/gtk/gtkpasswordentry.c
+++ b/gtk/gtkpasswordentry.c
@@ -26,9 +26,12 @@
#include "gtkbindings.h"
#include "gtktextprivate.h"
#include "gtkeditable.h"
+#include "gtkgesturemultipress.h"
#include "gtkbox.h"
#include "gtkimage.h"
+#include "gtkcheckmenuitem.h"
#include "gtkintl.h"
+#include "gtkprivate.h"
#include "gtkmarshalers.h"
#include "gtkstylecontext.h"
#include "gtkeventcontrollerkey.h"
@@ -54,8 +57,16 @@ typedef struct {
GtkWidget *entry;
GtkWidget *icon;
GdkKeymap *keymap;
+ GtkWidget *peek_icon;
} GtkPasswordEntryPrivate;
+enum {
+ PROP_SHOW_PEEK_ICON = 1,
+ NUM_PROPERTIES
+};
+
+static GParamSpec *props[NUM_PROPERTIES] = { NULL, };
+
static void gtk_password_entry_editable_init (GtkEditableInterface *iface);
G_DEFINE_TYPE_WITH_CODE (GtkPasswordEntry, gtk_password_entry, GTK_TYPE_WIDGET,
@@ -87,6 +98,44 @@ focus_changed (GtkWidget *widget)
keymap_state_changed (priv->keymap, widget);
}
+static void
+gtk_password_entry_toggle_peek (GtkPasswordEntry *entry)
+{
+ GtkPasswordEntryPrivate *priv = gtk_password_entry_get_instance_private (entry);
+
+ if (gtk_text_get_visibility (GTK_TEXT (priv->entry)))
+ {
+ gtk_text_set_visibility (GTK_TEXT (priv->entry), FALSE);
+ gtk_image_set_from_icon_name (GTK_IMAGE (priv->peek_icon), "password-invisible");
+ }
+ else
+ {
+ gtk_text_set_visibility (GTK_TEXT (priv->entry), TRUE);
+ gtk_image_set_from_icon_name (GTK_IMAGE (priv->peek_icon), "password-visible");
+ }
+}
+
+static void
+populate_popup (GtkText *text,
+ GtkWidget *popup,
+ GtkPasswordEntry *entry)
+{
+ GtkPasswordEntryPrivate *priv = gtk_password_entry_get_instance_private (entry);
+
+ if (priv->peek_icon != NULL)
+ {
+ GtkWidget *item;
+
+ item = gtk_check_menu_item_new_with_mnemonic (_("_Show text"));
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),
+ gtk_text_get_visibility (text));
+ g_signal_connect_swapped (item, "activate",
+ G_CALLBACK (gtk_password_entry_toggle_peek), entry);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (popup), item);
+ }
+}
+
static void
gtk_password_entry_init (GtkPasswordEntry *entry)
{
@@ -106,6 +155,7 @@ gtk_password_entry_init (GtkPasswordEntry *entry)
gtk_container_add (GTK_CONTAINER (priv->box), priv->entry);
gtk_editable_init_delegate (GTK_EDITABLE (entry));
g_signal_connect_swapped (priv->entry, "notify::has-focus", G_CALLBACK (focus_changed), entry);
+ g_signal_connect (priv->entry, "populate-popup", G_CALLBACK (populate_popup), entry);
priv->icon = gtk_image_new_from_icon_name ("dialog-warning-symbolic");
gtk_widget_set_tooltip_text (priv->icon, _("Caps Lock is on"));
@@ -141,6 +191,7 @@ gtk_password_entry_dispose (GObject *object)
g_clear_pointer (&priv->entry, gtk_widget_unparent);
g_clear_pointer (&priv->icon, gtk_widget_unparent);
+ g_clear_pointer (&priv->peek_icon, gtk_widget_unparent);
g_clear_pointer (&priv->box, gtk_widget_unparent);
G_OBJECT_CLASS (gtk_password_entry_parent_class)->dispose (object);
@@ -158,10 +209,21 @@ gtk_password_entry_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
+ GtkPasswordEntry *entry = GTK_PASSWORD_ENTRY (object);
+
if (gtk_editable_delegate_set_property (object, prop_id, value, pspec))
return;
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ switch (prop_id)
+ {
+ case PROP_SHOW_PEEK_ICON:
+ gtk_password_entry_set_show_peek_icon (entry, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
@@ -170,10 +232,21 @@ gtk_password_entry_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
+ GtkPasswordEntry *entry = GTK_PASSWORD_ENTRY (object);
+
if (gtk_editable_delegate_get_property (object, prop_id, value, pspec))
return;
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ switch (prop_id)
+ {
+ case PROP_SHOW_PEEK_ICON:
+ g_value_set_boolean (value, gtk_password_entry_get_show_peek_icon (entry));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
@@ -257,7 +330,15 @@ gtk_password_entry_class_init (GtkPasswordEntryClass *klass)
widget_class->grab_focus = gtk_password_entry_grab_focus;
widget_class->mnemonic_activate = gtk_password_entry_mnemonic_activate;
- gtk_editable_install_properties (object_class, 1);
+ props[PROP_SHOW_PEEK_ICON] =
+ g_param_spec_boolean ("show-peek-icon",
+ P_("Show Peek Icon"),
+ P_("Whether to show an icon for revealing the content"),
+ FALSE,
+ GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, props);
+ gtk_editable_install_properties (object_class, NUM_PROPERTIES);
gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_ENTRY_ACCESSIBLE);
gtk_widget_class_set_css_name (widget_class, I_("entry"));
@@ -290,3 +371,47 @@ gtk_password_entry_new (void)
{
return GTK_WIDGET (g_object_new (GTK_TYPE_PASSWORD_ENTRY, NULL));
}
+
+void
+gtk_password_entry_set_show_peek_icon (GtkPasswordEntry *entry,
+ gboolean show_peek_icon)
+{
+ GtkPasswordEntryPrivate *priv = gtk_password_entry_get_instance_private (entry);
+
+ g_return_if_fail (GTK_IS_PASSWORD_ENTRY (entry));
+
+ if (show_peek_icon == (priv->peek_icon != NULL))
+ return;
+
+ if (show_peek_icon)
+ {
+ GtkGesture *press;
+
+ priv->peek_icon = gtk_image_new_from_icon_name ("password-invisible");
+ gtk_style_context_add_class (gtk_widget_get_style_context (priv->peek_icon), "clickable");
+ gtk_widget_set_tooltip_text (priv->peek_icon, _("Show text"));
+ gtk_container_add (GTK_CONTAINER (priv->box), priv->peek_icon);
+
+ press = gtk_gesture_multi_press_new ();
+ g_signal_connect_swapped (press, "released",
+ G_CALLBACK (gtk_password_entry_toggle_peek), entry);
+ gtk_widget_add_controller (priv->peek_icon, GTK_EVENT_CONTROLLER (press));
+ }
+ else
+ {
+ g_clear_pointer (&priv->peek_icon, gtk_widget_unparent);
+ gtk_text_set_visibility (GTK_TEXT (priv->entry), FALSE);
+ }
+
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_SHOW_PEEK_ICON]);
+}
+
+gboolean
+gtk_password_entry_get_show_peek_icon (GtkPasswordEntry *entry)
+{
+ GtkPasswordEntryPrivate *priv = gtk_password_entry_get_instance_private (entry);
+
+ g_return_val_if_fail (GTK_IS_PASSWORD_ENTRY (entry), FALSE);
+
+ return priv->peek_icon != NULL;
+}
diff --git a/gtk/gtkpasswordentry.h b/gtk/gtkpasswordentry.h
index 66de3ee961..2527c58ca2 100644
--- a/gtk/gtkpasswordentry.h
+++ b/gtk/gtkpasswordentry.h
@@ -55,6 +55,12 @@ GType gtk_password_entry_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GtkWidget * gtk_password_entry_new (void);
+GDK_AVAILABLE_IN_ALL
+void gtk_password_entry_set_show_peek_icon (GtkPasswordEntry *entry,
+ gboolean show_peek_icon);
+GDK_AVAILABLE_IN_ALL
+gboolean gtk_password_entry_get_show_peek_icon (GtkPasswordEntry *entry);
+
G_END_DECLS
#endif /* __GTK_PASSWORD_ENTRY_H__ */
diff --git a/gtk/icons/16x16/status/password-invisible-symbolic.symbolic.png
b/gtk/icons/16x16/status/password-invisible-symbolic.symbolic.png
new file mode 100644
index 0000000000..500525e817
Binary files /dev/null and b/gtk/icons/16x16/status/password-invisible-symbolic.symbolic.png differ
diff --git a/gtk/icons/16x16/status/password-visible-symbolic.symbolic.png
b/gtk/icons/16x16/status/password-visible-symbolic.symbolic.png
new file mode 100644
index 0000000000..b64050770d
Binary files /dev/null and b/gtk/icons/16x16/status/password-visible-symbolic.symbolic.png differ
diff --git a/gtk/icons/scalable/status/password-invisible-symbolic.svg
b/gtk/icons/scalable/status/password-invisible-symbolic.svg
new file mode 100644
index 0000000000..8989e833df
--- /dev/null
+++ b/gtk/icons/scalable/status/password-invisible-symbolic.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <g color="#bebebe" fill="#474747">
+ <path d="M3 7h10c.554 0 1 .446 1 1v3c0 .554-.446 1-1 1H3c-.554 0-1-.446-1-1V8c0-.554.446-1 1-1z"
style="marker:none" overflow="visible"/>
+ <path d="M7 1s-.709-.014-1.447.355C4.814 1.725 4 2.667 4 4v4h2V4c0-.667.186-.725.447-.855C6.71 3.014
7 3 7 3h2s.291.014.553.145c.261.13.447.188.447.855v4h2V4c0-1.333-.814-2.275-1.553-2.645C9.71.986 9 1 9 1z"
style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;shape-padding:0;isolation:auto;mix-blend-mode:normal;marker:none"
font-weight="400" font-family="sans-serif" white-space="normal" overflow="visible"/>
+ <path d="M2 10h12v4H2z" style="marker:none" overflow="visible"/>
+ </g>
+</svg>
diff --git a/gtk/icons/scalable/status/password-visible-symbolic.svg
b/gtk/icons/scalable/status/password-visible-symbolic.svg
new file mode 100644
index 0000000000..5857953ccf
--- /dev/null
+++ b/gtk/icons/scalable/status/password-visible-symbolic.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+ <g color="#bebebe" fill="#474747">
+ <path d="M3 9h10c.554 0 1 .446 1 1v3c0 .554-.446 1-1 1H3c-.554 0-1-.446-1-1v-3c0-.554.446-1 1-1z"
style="marker:none" overflow="visible"/>
+ <path d="M7 0s-.709-.014-1.447.356C4.814.725 4 1.666 4 3v3h2V3c0-.667.186-.725.447-.855C6.71 2.014 7
2 7 2h2s.291.014.553.145c.261.13.447.188.447.855v8h2V3c0-1.333-.814-2.275-1.553-2.644C9.71-.014 9 0 9 0z"
style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;shape-padding:0;isolation:auto;mix-blend-mode:normal;marker:none"
font-weight="400" font-family="sans-serif" white-space="normal" overflow="visible"/>
+ <path d="M2 12h12v4H2z" style="marker:none" overflow="visible"/>
+ </g>
+</svg>
diff --git a/tests/testentryicons.c b/tests/testentryicons.c
index 2dc2756ddb..07c9fb6ebc 100644
--- a/tests/testentryicons.c
+++ b/tests/testentryicons.c
@@ -242,6 +242,7 @@ main (int argc, char **argv)
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
entry = gtk_password_entry_new ();
+ gtk_password_entry_set_show_peek_icon (GTK_PASSWORD_ENTRY (entry), TRUE);
gtk_widget_set_hexpand (entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), entry, 1, 3, 1, 1);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]