[libhandy/hdy_password_entry: 60/62] Add HdyPasswordEntry widget
- From: Alexander Mikhaylenko <alexm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libhandy/hdy_password_entry: 60/62] Add HdyPasswordEntry widget
- Date: Fri, 22 May 2020 13:38:09 +0000 (UTC)
commit f493c24aaad6df5307439b1ced67f26b168fc602
Author: Ujjwal Kumar <ujjwalkumar0501 gmail com>
Date: Tue Mar 31 21:21:57 2020 +0530
Add HdyPasswordEntry widget
src/handy.gresources.xml | 2 +
src/handy.h | 1 +
src/hdy-password-entry.c | 484 +++++++++++++++++++++++++++++++++++++++++++++++
src/hdy-password-entry.h | 36 ++++
src/meson.build | 2 +
5 files changed, 525 insertions(+)
---
diff --git a/src/handy.gresources.xml b/src/handy.gresources.xml
index f19ba862..33b17881 100644
--- a/src/handy.gresources.xml
+++ b/src/handy.gresources.xml
@@ -2,6 +2,8 @@
<gresources>
<gresource prefix="/sm/puri/handy">
<file preprocess="xml-stripblanks">icons/hdy-expander-arrow-symbolic.svg</file>
+ <file preprocess="xml-stripblanks">icons/hdy-eye-not-looking-symbolic.svg</file>
+ <file preprocess="xml-stripblanks">icons/hdy-eye-open-symbolic.svg</file>
<file compressed="true">themes/Adwaita.css</file>
<file compressed="true">themes/Adwaita-dark.css</file>
<file compressed="true">themes/fallback.css</file>
diff --git a/src/handy.h b/src/handy.h
index 9e6389f0..2bf36576 100644
--- a/src/handy.h
+++ b/src/handy.h
@@ -42,6 +42,7 @@ G_BEGIN_DECLS
#include "hdy-leaflet.h"
#include "hdy-list-box.h"
#include "hdy-navigation-direction.h"
+#include "hdy-password-entry.h"
#include "hdy-preferences-group.h"
#include "hdy-preferences-page.h"
#include "hdy-preferences-row.h"
diff --git a/src/hdy-password-entry.c b/src/hdy-password-entry.c
new file mode 100644
index 00000000..004ac35c
--- /dev/null
+++ b/src/hdy-password-entry.c
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2019 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include "hdy-password-entry.h"
+
+#define DEFAULT_PEEK_DURATION 2000
+#define MIN_PEEK_DURATION 500
+
+/**
+ * SECTION:hdy-password-entry
+ * @short_description: A password entry widget.
+ * @title: HdyPasswordEntry
+ *
+ * The #HdyPasswordEntry widget is a password entry widget with toggle
+ * functionality to show entered characters for a short duration of time.
+ *
+ * # CSS nodes
+ *
+ * #HdyPasswordEntry has a single CSS node with name passwordentry.
+ *
+ * Since: 1.0
+ */
+
+typedef struct
+{
+ GtkWidget *password_entry;
+ GtkWidget *peek_icon;
+ GtkWidget *peek_icon_event_box;
+
+ GtkCssProvider *css_provider;
+
+ gboolean show_peek_icon;
+ guint peek_duration;
+ guint timeout_id;
+} HdyPasswordEntryPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (HdyPasswordEntry, hdy_password_entry, GTK_TYPE_OVERLAY);
+
+enum {
+ PROP_0,
+ PROP_PEEK_DURATION,
+ PROP_SHOW_PEEK_ICON,
+ LAST_PROP
+};
+
+enum {
+ SIGNAL_TIMED_OUT,
+ SIGNAL_LAST
+};
+
+static GParamSpec *props[LAST_PROP];
+static guint signals[SIGNAL_LAST];
+
+static void hide_text (GtkWidget*, gpointer);
+static void show_text (GtkWidget*, gpointer);
+
+static gboolean
+timeout_cb (gpointer user_data)
+{
+ hide_text (NULL, user_data);
+
+ g_signal_emit (HDY_PASSWORD_ENTRY (user_data), signals[SIGNAL_TIMED_OUT], 0);
+
+ return TRUE;
+}
+
+static void
+set_timeout (HdyPasswordEntry *entry)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (entry);
+
+ if (priv->timeout_id > 0)
+ g_source_remove (priv->timeout_id);
+ priv->timeout_id = g_timeout_add (priv->peek_duration,
+ timeout_cb,
+ entry);
+ g_source_set_name_by_id (priv->timeout_id, "[hdy] hdy_password_entry_peek_duration_cb");
+}
+
+static void
+peek_icon_button_press_event_cb (GtkWidget *event_box,
+ GdkEventButton *event,
+ HdyPasswordEntry *entry)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (entry);
+
+ if ((event->type == GDK_BUTTON_PRESS &&
+ event->button == 1) ||
+ (event->type == GDK_TOUCH_BEGIN))
+ {
+ if (priv->timeout_id > 0)
+ hide_text (NULL, entry);
+ else
+ show_text (NULL, entry);
+ }
+}
+
+static void
+password_entry_size_allocated_cb (GtkWidget *widget,
+ GdkRectangle *allocation,
+ gpointer user_data)
+{
+ GtkStyleContext *style_context;
+ GtkStateFlags state_flags;
+ GtkBorder border, margin, padding;
+ guint spacing;
+
+ style_context = gtk_widget_get_style_context (widget);
+ state_flags = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_border (style_context, state_flags, &border);
+ gtk_style_context_get_margin (style_context, state_flags, &margin);
+ gtk_style_context_get_padding (style_context, state_flags, &padding);
+
+ spacing = margin.right + border.right;
+
+ if (padding.right == padding.left)
+ spacing += padding.right;
+ else
+ spacing += padding.right < padding.left? padding.right: padding.left;
+ gtk_widget_set_margin_end (GTK_WIDGET (user_data), spacing);
+}
+
+static void
+event_box_size_allocated_cb (GtkWidget *widget,
+ GdkRectangle *allocation,
+ gpointer user_data)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (HDY_PASSWORD_ENTRY (user_data));
+ g_autofree gchar *css;
+ GtkBorder padding;
+ guint extra_space;
+
+ gtk_style_context_get_padding (gtk_widget_get_style_context (priv->password_entry),
+ gtk_widget_get_state_flags (priv->password_entry),
+ &padding);
+ extra_space = allocation->width + (padding.right > padding.left ?
+ padding.left:
+ padding.right);
+
+ /* Don't add this padding when all the widgets are hidden (in which case
+ * the allocated width is 1), because it distorts the appearance of widget.
+ */
+ if (allocation->width > 6)
+ extra_space += 6;
+
+ css = g_strdup_printf (".password_entry:dir(ltr) { padding-right: %dpx; }"\
+ ".password_entry:dir(rtl) { padding-left: %dpx; }",
+ extra_space, extra_space);
+
+ gtk_css_provider_load_from_data (priv->css_provider, css, -1, NULL);
+}
+
+static void
+hide_text (GtkWidget *widget,
+ gpointer user_data)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (HDY_PASSWORD_ENTRY (user_data));
+
+ if (priv->timeout_id > 0)
+ g_source_remove (priv->timeout_id);
+ gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), FALSE);
+ gtk_image_set_from_icon_name (GTK_IMAGE (priv->peek_icon),
+ "hdy-eye-not-looking-symbolic",
+ GTK_ICON_SIZE_MENU);
+ gtk_widget_set_tooltip_text (priv->peek_icon_event_box, _("Show text"));
+ priv->timeout_id = 0;
+}
+
+static void
+show_text (GtkWidget *widget,
+ gpointer user_data)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (HDY_PASSWORD_ENTRY (user_data));
+
+ gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), TRUE);
+ gtk_image_set_from_icon_name (GTK_IMAGE (priv->peek_icon),
+ "hdy-eye-open-symbolic",
+ GTK_ICON_SIZE_MENU);
+ gtk_widget_set_tooltip_text (priv->peek_icon_event_box, _("Hide text"));
+ set_timeout (HDY_PASSWORD_ENTRY (user_data));
+}
+
+static void
+populate_popup_cb (GtkEntry *password_entry,
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget *menuitem;
+
+ g_assert (GTK_IS_MENU (widget));
+
+ if (gtk_entry_get_visibility (password_entry))
+ {
+ menuitem = gtk_menu_item_new_with_mnemonic (_("_Hide text"));
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (hide_text), user_data);
+ }
+ else
+ {
+ menuitem = gtk_menu_item_new_with_mnemonic (_("_Show text"));
+ g_signal_connect (menuitem, "activate",
+ G_CALLBACK (show_text), user_data);
+ }
+
+ gtk_widget_set_sensitive (menuitem, TRUE);
+
+ gtk_widget_show (menuitem);
+ gtk_menu_shell_append (GTK_MENU_SHELL (widget), menuitem);
+ gtk_widget_show (menuitem);
+}
+
+static void
+hdy_password_entry_dispose (GObject *object)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private
+ (HDY_PASSWORD_ENTRY (object));
+
+ if (priv->timeout_id > 0)
+ g_source_remove (priv->timeout_id);
+
+ g_clear_object (&priv->css_provider);
+
+ G_OBJECT_CLASS (hdy_password_entry_parent_class)->dispose (object);
+}
+
+static void
+hdy_password_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ HdyPasswordEntry *self = HDY_PASSWORD_ENTRY (object);
+
+ switch (prop_id) {
+ case PROP_PEEK_DURATION:
+ g_value_set_uint (value, hdy_password_entry_get_peek_duration (self));
+ break;
+
+ case PROP_SHOW_PEEK_ICON:
+ g_value_set_boolean (value, hdy_password_entry_get_show_peek_icon (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+hdy_password_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ HdyPasswordEntry *self = HDY_PASSWORD_ENTRY (object);
+
+ switch (prop_id) {
+ case PROP_PEEK_DURATION:
+ hdy_password_entry_set_peek_duration (self, g_value_get_uint (value));
+ break;
+
+ case PROP_SHOW_PEEK_ICON:
+ hdy_password_entry_set_show_peek_icon (self, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+hdy_password_entry_class_init (HdyPasswordEntryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ /**
+ * HdyPasswordEntry::timed-out:
+ *
+ * This signal is emitted when a password peek timeout happens.
+ *
+ * Since: 1.0
+ */
+ signals[SIGNAL_TIMED_OUT] = g_signal_new ("timed-out",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 0);
+
+ object_class->dispose = hdy_password_entry_dispose;
+ object_class->get_property = hdy_password_entry_get_property;
+ object_class->set_property = hdy_password_entry_set_property;
+
+ /**
+ * HdyPasswordEntry::peek-duration:
+ *
+ * It has default value of 2000 Milliseconds and a Minimum
+ * duration of 500 Milliseconds.
+ *
+ * Changing this property sends a notify signal.
+ *
+ * Since: 1.0
+ */
+ props[PROP_PEEK_DURATION] = g_param_spec_uint
+ ("peek-duration",
+ _("Peek duration"),
+ _("Password peek duration in milliseconds"),
+ MIN_PEEK_DURATION,
+ G_MAXUINT,
+ DEFAULT_PEEK_DURATION,
+ G_PARAM_READWRITE);
+
+ /**
+ * HdyPasswordEntry::show-peek-icon:
+ *
+ * Whether to show the peek icon.
+ *
+ * Changing this property sends a notify signal.
+ *
+ * Since: 1.0
+ */
+ props[PROP_SHOW_PEEK_ICON] = g_param_spec_boolean
+ ("show-peek-icon",
+ _("Show peek icon"),
+ _("Whether to show the peek icon"),
+ TRUE,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, LAST_PROP, props);
+
+ gtk_widget_class_set_css_name (widget_class, "passwordentry");
+}
+
+static void
+hdy_password_entry_init (HdyPasswordEntry *entry)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (entry);
+
+ priv->peek_duration = DEFAULT_PEEK_DURATION;
+ priv->show_peek_icon = TRUE;
+ priv->password_entry = gtk_entry_new ();
+ priv->peek_icon = gtk_image_new_from_icon_name ("hdy-eye-not-looking-symbolic", GTK_ICON_SIZE_MENU);
+ priv->peek_icon_event_box = gtk_event_box_new ();
+
+ priv->css_provider = gtk_css_provider_new ();
+ gtk_style_context_add_provider (gtk_widget_get_style_context (priv->password_entry),
+ GTK_STYLE_PROVIDER (priv->css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+
+ gtk_widget_set_halign (priv->peek_icon_event_box, GTK_ALIGN_END);
+ gtk_widget_set_tooltip_text (priv->peek_icon_event_box, _("Show text"));
+ g_signal_connect (G_OBJECT (priv->peek_icon_event_box), "button-press-event",
+ G_CALLBACK (peek_icon_button_press_event_cb), entry);
+
+ gtk_widget_set_valign (priv->peek_icon, GTK_ALIGN_CENTER);
+
+ g_signal_connect (G_OBJECT (priv->peek_icon_event_box), "size-allocate",
+ G_CALLBACK (event_box_size_allocated_cb), entry);
+ g_signal_connect (G_OBJECT (priv->password_entry), "size-allocate",
+ G_CALLBACK (password_entry_size_allocated_cb),
+ priv->peek_icon_event_box);
+ g_signal_connect (G_OBJECT (priv->password_entry), "populate-popup",
+ G_CALLBACK (populate_popup_cb), entry);
+
+ gtk_style_context_add_class (gtk_widget_get_style_context (priv->password_entry), "password_entry");
+ g_object_set (G_OBJECT (priv->password_entry),
+ "input-purpose", GTK_INPUT_PURPOSE_PASSWORD,
+ "visibility", FALSE,
+ "placeholder-text", _("Password"),
+ "can-focus", TRUE,
+ NULL);
+
+ gtk_overlay_add_overlay (GTK_OVERLAY (entry), priv->peek_icon_event_box);
+ gtk_container_add (GTK_CONTAINER (entry), priv->password_entry);
+ gtk_container_add (GTK_CONTAINER (priv->peek_icon_event_box), priv->peek_icon);
+
+ gtk_widget_show (priv->password_entry);
+ gtk_widget_show (priv->peek_icon_event_box);
+ gtk_widget_show (priv->peek_icon);
+}
+
+/**
+ *hdy_password_entry_get_peek_duration:
+ * @entry: a HdyPasswordEntry
+ *
+ * Gets peek duration of password characters.
+ *
+ * Returns: Peek duration in milliseconds
+ *
+ * Since: 1.0
+ */
+
+guint
+hdy_password_entry_get_peek_duration (HdyPasswordEntry *entry)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (entry);
+
+ return priv->peek_duration;
+}
+
+/**
+ * hdy_password_entry_set_peek_duration:
+ * @entry: a HdyPasswordEntry
+ * @peek_duration: a guint
+ *
+ * Sets peek duration of password characters.
+ *
+ * Since: 1.0
+ */
+void
+hdy_password_entry_set_peek_duration (HdyPasswordEntry *entry,
+ guint peek_duration)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (entry);
+
+ priv->peek_duration = peek_duration;
+ g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_PEEK_DURATION]);
+}
+
+/**
+ * hdy_password_entry_get_show_peek_icon:
+ * @entry: a HdyPasswordEntry
+ *
+ * Returns whether the entry is showing a clickable icon
+ * to reveal the contents of the entry in clear text.
+ *
+ * Since: 1.0
+ */
+gboolean
+hdy_password_entry_get_show_peek_icon (HdyPasswordEntry *entry)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (entry);
+
+ return priv->show_peek_icon;
+}
+
+/**
+ * hdy_password_entry_set_show_peek_icon:
+ * @entry: a HdyPasswordEntry
+ * @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.
+ *
+ * Since: 1.0
+ */
+void
+hdy_password_entry_set_show_peek_icon (HdyPasswordEntry *entry,
+ gboolean show_peek_icon)
+{
+ HdyPasswordEntryPrivate *priv = hdy_password_entry_get_instance_private (entry);
+
+ if (show_peek_icon)
+ gtk_widget_show (priv->peek_icon);
+ else
+ gtk_widget_hide (priv->peek_icon);
+
+ priv->show_peek_icon = show_peek_icon;
+}
+
+/**
+ * hdy_password_entry_new:
+ *
+ * Create a new #HdyPasswordEntry widget.
+ *
+ * Returns: The newly created #HdyPasswordEntry widget
+ *
+ * Since: 1.0
+ */
+GtkWidget *
+hdy_password_entry_new (void)
+{
+ return GTK_WIDGET (g_object_new (HDY_TYPE_PASSWORD_ENTRY, NULL));
+}
+
diff --git a/src/hdy-password-entry.h b/src/hdy-password-entry.h
new file mode 100644
index 00000000..a0fe9af4
--- /dev/null
+++ b/src/hdy-password-entry.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 Purism SPC
+ *
+ * SPDX-License-Identifier: LGPL-2.1+
+ */
+
+#pragma once
+
+#if !defined(_HANDY_INSIDE) && !defined(HANDY_COMPILATION)
+#error "Only <handy.h> can be included directly."
+#endif
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define HDY_TYPE_PASSWORD_ENTRY (hdy_password_entry_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (HdyPasswordEntry, hdy_password_entry, HDY, PASSWORD_ENTRY, GtkOverlay)
+
+struct _HdyPasswordEntryClass
+{
+ GtkOverlayClass parent_class;
+};
+
+GtkWidget *hdy_password_entry_new (void);
+guint hdy_password_entry_get_peek_duration (HdyPasswordEntry *entry);
+void hdy_password_entry_set_peek_duration (HdyPasswordEntry *entry,
+ guint peek_duration);
+
+gboolean hdy_password_entry_get_show_peek_icon (HdyPasswordEntry *entry);
+void hdy_password_entry_set_show_peek_icon (HdyPasswordEntry *entry,
+ gboolean show_peek_icon);
+
+
+G_END_DECLS
diff --git a/src/meson.build b/src/meson.build
index e11d95cb..fdf14d1f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -82,6 +82,7 @@ src_headers = [
'hdy-leaflet.h',
'hdy-list-box.h',
'hdy-navigation-direction.h',
+ 'hdy-password-entry.h',
'hdy-preferences-group.h',
'hdy-preferences-page.h',
'hdy-preferences-row.h',
@@ -132,6 +133,7 @@ src_sources = [
'hdy-main.c',
'hdy-navigation-direction.c',
'hdy-nothing.c',
+ 'hdy-password-entry.c',
'hdy-preferences-group.c',
'hdy-preferences-page.c',
'hdy-preferences-row.c',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]