diff options
author | Matthias Clasen <mclasen@redhat.com> | 2019-03-13 16:27:07 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2019-03-14 20:31:45 -0400 |
commit | a3b73a416efdb15f91c957a7b7e7b7a77a0d8130 (patch) | |
tree | 6a6c203297f149c20be024c6cab3b8c4869815cb | |
parent | d3cecd65a5da94711315269415ef24f3896ce622 (diff) | |
download | gtk+-a3b73a416efdb15f91c957a7b7e7b7a77a0d8130.tar.gz |
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.
-rw-r--r-- | docs/reference/gtk/gtk4-sections.txt | 2 | ||||
-rw-r--r-- | gtk/gtkpasswordentry.c | 136 | ||||
-rw-r--r-- | gtk/gtkpasswordentry.h | 6 | ||||
-rw-r--r-- | gtk/icons/16x16/status/eye-not-looking-symbolic.symbolic.png | bin | 0 -> 324 bytes | |||
-rw-r--r-- | gtk/icons/16x16/status/eye-open-negative-filled-symbolic.symbolic.png | bin | 0 -> 295 bytes | |||
-rw-r--r-- | gtk/icons/scalable/status/eye-not-looking-symbolic.svg | 3 | ||||
-rw-r--r-- | gtk/icons/scalable/status/eye-open-negative-filled-symbolic.svg | 26 | ||||
-rw-r--r-- | tests/testentryicons.c | 1 |
8 files changed, 167 insertions, 7 deletions
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt index 911135eb39..9d3e68f683 100644 --- a/docs/reference/gtk/gtk4-sections.txt +++ b/docs/reference/gtk/gtk4-sections.txt @@ -995,6 +995,8 @@ gtk_entry_get_type <TITLE>GtkPasswordEntry</TITLE> GtkPasswordEntry gtk_password_entry_new +gtk_password_entry_set_show_peek_icon +gtk_password_entry_get_show_peek_icon <SUBSECTION Private> gtk_password_entry_get_type </SECTION> diff --git a/gtk/gtkpasswordentry.c b/gtk/gtkpasswordentry.c index 08467ad509..7dc6603a59 100644 --- a/gtk/gtkpasswordentry.c +++ b/gtk/gtkpasswordentry.c @@ -26,8 +26,10 @@ #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" @@ -41,13 +43,14 @@ * @Short_description: An entry for secrets * @Title: GtkPasswordEntry * - * #GtkPasswordEntry is entry that has been tailored for - * entering secrets. It does not show its contents in clear text, - * does not allow to copy it to the clipboard, and it shows a - * warning when Caps-Lock is engaged. + * #GtkPasswordEntry is entry that has been tailored for entering secrets. + * It does not show its contents in clear text, does not allow to copy it + * to the clipboard, and it shows a warning when Caps Lock is engaged. * - * GtkPasswordEntry provides no API of its own and should be used - * with the #GtkEditable API. + * Optionally, it can offer a way to reveal the contents in clear text. + * + * GtkPasswordEntry provides only minimal API and should be used with the + * #GtkEditable API. */ typedef struct { @@ -55,11 +58,13 @@ typedef struct { GtkWidget *entry; GtkWidget *icon; GdkKeymap *keymap; + GtkWidget *peek_icon; } GtkPasswordEntryPrivate; enum { PROP_PLACEHOLDER_TEXT = 1, PROP_ACTIVATES_DEFAULT, + PROP_SHOW_PEEK_ICON, NUM_PROPERTIES }; @@ -97,13 +102,53 @@ focus_changed (GtkWidget *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), "eye-not-looking-symbolic"); + gtk_widget_set_tooltip_text (priv->peek_icon, _("Show text")); + } + else + { + gtk_text_set_visibility (GTK_TEXT (priv->entry), TRUE); + gtk_image_set_from_icon_name (GTK_IMAGE (priv->peek_icon), "eye-open-negative-filled-symbolic"); + gtk_widget_set_tooltip_text (priv->peek_icon, _("Hide text")); + } +} + +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) { GtkPasswordEntryPrivate *priv = gtk_password_entry_get_instance_private (entry); gtk_widget_set_has_surface (GTK_WIDGET (entry), FALSE); - priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2); gtk_widget_set_hexpand (priv->box, FALSE); gtk_widget_set_vexpand (priv->box, FALSE); gtk_widget_set_parent (priv->box, GTK_WIDGET (entry)); @@ -115,6 +160,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 ("caps-lock-symbolic"); gtk_widget_set_tooltip_text (priv->icon, _("Caps Lock is on")); @@ -152,6 +198,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); @@ -183,6 +230,9 @@ gtk_password_entry_set_property (GObject *object, case PROP_ACTIVATES_DEFAULT: gtk_text_set_activates_default (GTK_TEXT (priv->entry), g_value_get_boolean (value)); + + case PROP_SHOW_PEEK_ICON: + gtk_password_entry_set_show_peek_icon (entry, g_value_get_boolean (value)); break; default: @@ -211,6 +261,9 @@ gtk_password_entry_get_property (GObject *object, case PROP_ACTIVATES_DEFAULT: g_value_set_boolean (value, gtk_text_get_activates_default (GTK_TEXT (priv->entry))); + + case PROP_SHOW_PEEK_ICON: + g_value_set_boolean (value, gtk_password_entry_get_show_peek_icon (entry)); break; default: @@ -314,6 +367,13 @@ gtk_password_entry_class_init (GtkPasswordEntryClass *klass) FALSE, GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); + 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); @@ -348,3 +408,65 @@ gtk_password_entry_new (void) { return GTK_WIDGET (g_object_new (GTK_TYPE_PASSWORD_ENTRY, NULL)); } + +/** + * gtk_password_entry_set_show_peek_icon: + * @entry: a #GtkPasswordEntry + * show_peek_icon: whether to show the peek icon + * + * Sets whether the entry should have a clickable icon + * to show the contents of the entry in clear text. + * + * Setting this to %FALSE also hides the text again. + */ +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 ("eye-not-looking-symbolic"); + 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]); +} + +/** + * gtk_password_entry_get_show_peek_icon: + * @entry: a #GtkPasswordEntry + * + * Returns whether the entry is showing a clickable icon + * to reveal the contents of the entry in clear text. + * + * Returns: %TRUE if an icon is shown + */ +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/eye-not-looking-symbolic.symbolic.png b/gtk/icons/16x16/status/eye-not-looking-symbolic.symbolic.png Binary files differnew file mode 100644 index 0000000000..3773d3cc30 --- /dev/null +++ b/gtk/icons/16x16/status/eye-not-looking-symbolic.symbolic.png diff --git a/gtk/icons/16x16/status/eye-open-negative-filled-symbolic.symbolic.png b/gtk/icons/16x16/status/eye-open-negative-filled-symbolic.symbolic.png Binary files differnew file mode 100644 index 0000000000..b642dd7566 --- /dev/null +++ b/gtk/icons/16x16/status/eye-open-negative-filled-symbolic.symbolic.png diff --git a/gtk/icons/scalable/status/eye-not-looking-symbolic.svg b/gtk/icons/scalable/status/eye-not-looking-symbolic.svg new file mode 100644 index 0000000000..792a22ad8a --- /dev/null +++ b/gtk/icons/scalable/status/eye-not-looking-symbolic.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"> + <path d="M13.98 1.99a1 1 0 0 0-.687.303l-.984.984A8 8 0 0 0 8 2 8 8 0 0 0 .262 8.01a8 8 0 0 0 2.943 4.37l-.912.913a1 1 0 1 0 1.414 1.414l11-11a1 1 0 0 0-.727-1.717zM8 4a4 4 0 0 1 2.611.974l-1.42 1.42A2 2 0 0 0 8 6a2 2 0 0 0-2 2 2 2 0 0 0 .396 1.19l-1.42 1.42A4 4 0 0 1 4 8a4 4 0 0 1 4-4zm7.03 2.209l-3.344 3.343a4 4 0 0 1-2.127 2.127l-2.28 2.28a8 8 0 0 0 .721.04 8 8 0 0 0 7.738-6.01 8 8 0 0 0-.709-1.78zm-7.53.79a.5.5 0 0 1 .5.5.5.5 0 0 1-.5.5.5.5 0 0 1-.5-.5.5.5 0 0 1 .5-.5z" fill="#2e3436"/> +</svg>
\ No newline at end of file diff --git a/gtk/icons/scalable/status/eye-open-negative-filled-symbolic.svg b/gtk/icons/scalable/status/eye-open-negative-filled-symbolic.svg new file mode 100644 index 0000000000..f4e133a928 --- /dev/null +++ b/gtk/icons/scalable/status/eye-open-negative-filled-symbolic.svg @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="16" viewBox="0 0 16 16" version="1.1" id="svg7384" height="16"> + <metadata id="metadata90"> + <rdf:RDF> + <cc:Work rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> + <dc:title>Gnome Symbolic Icon Theme</dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <title id="title9167">Gnome Symbolic Icon Theme</title> + <defs id="defs7386"> + <linearGradient osb:paint="solid" id="linearGradient7212"> + <stop style="stop-color:#000000;stop-opacity:1;" offset="0" id="stop7214"/> + </linearGradient> + </defs> + <g transform="translate(-341.0002,-13.000323)" style="display:inline" id="layer9"/> + <g transform="translate(-100,-380.00032)" id="layer1"/> + <g transform="translate(-100,-380.00032)" style="display:inline" id="layer10"> + <path d="m 108,382 a 8,8 0 0 0 -7.73828,6.00977 A 8,8 0 0 0 108,394 8,8 0 0 0 115.73828,387.99023 8,8 0 0 0 108,382 Z m 0,2 a 4,4 0 0 1 4,4 4,4 0 0 1 -4,4 4,4 0 0 1 -4,-4 4,4 0 0 1 4,-4 z" id="path2314" style="opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"/> + <path id="path2318" d="m 110,388.00003 a 2,2 0 0 1 -2,2 2,2 0 0 1 -2,-2 2,2 0 0 1 2,-2 2,2 0 0 1 2,2 z" style="vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/> + </g> + <g transform="translate(-100,-380.00032)" id="g6387"/> + <g transform="translate(-100,-380.00032)" id="layer11"/> +</svg>
\ No newline at end of file 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); |