[gnome-initial-setup/wip/feborges/generate-user-pics: 1/4] account: Generate a default user profile picture



commit b59079bdb5621cd2da9664eabacfbf00f947f963
Author: Felipe Borges <felipeborges gnome org>
Date:   Tue Oct 23 11:41:28 2018 +0200

    account: Generate a default user profile picture
    
    Generate a default user account picture composed of a background
    color and the user's initials.
    
    The background color is defined by mapping the hash of the user
    name with an existing colour in our palette[0].
    
    A new avatar is generated whenever the user full name entry focuses
    out.
    
    These changes are based on the mockups for the Settings Users panel[1].
    
    [0] https://gitlab.gnome.org/Community/Design/HIG-app-icons/blob/master/GNOME%20HIG.gpl
    [1] https://wiki.gnome.org/Design/OS/AvatarChooser#Tentative_Design
    
    Fixes #6

 .../pages/account/gis-account-avatar-chooser.ui    |   9 ++
 .../pages/account/gis-account-page-local.c         |   2 +
 .../pages/account/um-photo-dialog.c                |  37 +++++++
 .../pages/account/um-photo-dialog.h                |   3 +
 gnome-initial-setup/pages/account/um-utils.c       | 118 +++++++++++++++++++++
 gnome-initial-setup/pages/account/um-utils.h       |   3 +
 6 files changed, 172 insertions(+)
---
diff --git a/gnome-initial-setup/pages/account/gis-account-avatar-chooser.ui 
b/gnome-initial-setup/pages/account/gis-account-avatar-chooser.ui
index 7ab6a18..871470b 100644
--- a/gnome-initial-setup/pages/account/gis-account-avatar-chooser.ui
+++ b/gnome-initial-setup/pages/account/gis-account-avatar-chooser.ui
@@ -8,6 +8,15 @@
       <object class="GtkBox">
         <property name="visible">True</property>
         <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
+        <child>
+          <object class="GtkFlowBox" id="recent_pictures">
+            <property name="visible">True</property>
+            <property name="halign">start</property>
+            <property name="margin">20</property>
+            <property name="margin-bottom">0</property>
+            <property name="selection-mode">none</property>
+          </object>
+        </child>
         <child>
           <object class="GtkFlowBox" id="flowbox">
             <property name="visible">True</property>
diff --git a/gnome-initial-setup/pages/account/gis-account-page-local.c 
b/gnome-initial-setup/pages/account/gis-account-page-local.c
index e7a2cad..2c7bd9c 100644
--- a/gnome-initial-setup/pages/account/gis-account-page-local.c
+++ b/gnome-initial-setup/pages/account/gis-account-page-local.c
@@ -269,6 +269,8 @@ validate (GisAccountPageLocal *page)
   gtk_label_set_text (GTK_LABEL (priv->username_explanation), tip);
   g_free (tip);
 
+  um_photo_dialog_generate_avatar (priv->photo_dialog, name);
+
   validation_changed (page);
 
   return FALSE;
diff --git a/gnome-initial-setup/pages/account/um-photo-dialog.c 
b/gnome-initial-setup/pages/account/um-photo-dialog.c
index 4b6c38d..e4ac51b 100644
--- a/gnome-initial-setup/pages/account/um-photo-dialog.c
+++ b/gnome-initial-setup/pages/account/um-photo-dialog.c
@@ -46,13 +46,16 @@ struct _UmPhotoDialog {
         GtkWidget *popup_button;
         GtkWidget *take_picture_button;
         GtkWidget *flowbox;
+        GtkWidget *recent_pictures;
 
 #ifdef HAVE_CHEESE
         CheeseCameraDeviceMonitor *monitor;
         guint num_cameras;
 #endif /* HAVE_CHEESE */
 
+        GListStore *recent_faces;
         GListStore *faces;
+        GFile *generated_avatar;
 
         SelectAvatarCallback *callback;
         gpointer              data;
@@ -187,6 +190,15 @@ setup_photo_popup (UmPhotoDialog *um)
         g_signal_connect (um->flowbox, "child-activated",
                           G_CALLBACK (face_widget_activated), um);
 
+        um->recent_faces = g_list_store_new (G_TYPE_FILE);
+        gtk_flow_box_bind_model (GTK_FLOW_BOX (um->recent_pictures),
+                                 G_LIST_MODEL (um->recent_faces),
+                                 create_face_widget,
+                                 um,
+                                 NULL);
+        g_signal_connect (um->recent_pictures, "child-activated",
+                          G_CALLBACK (face_widget_activated), um);
+
         dirs = g_get_system_data_dirs ();
         for (i = 0; dirs[i] != NULL; i++) {
                 g_autoptr(GFileEnumerator) enumerator = NULL;
@@ -267,6 +279,30 @@ on_popup_button_button_pressed (GtkToggleButton *button,
         return FALSE;
 }
 
+void
+um_photo_dialog_generate_avatar (UmPhotoDialog *um,
+                                 const gchar   *name)
+{
+        cairo_surface_t *surface;
+        gchar *filename;
+
+        surface = generate_user_picture (name);
+
+        /* Save into a tmp file that later gets copied by AccountsService */
+        filename = g_build_filename (g_get_user_runtime_dir (), "avatar.png", NULL);
+        um->generated_avatar = g_file_new_for_path (filename);
+        cairo_surface_write_to_png (surface, g_file_get_path (um->generated_avatar));
+        g_free (filename);
+
+        /* Overwrite the first item */
+        if (g_list_model_get_item (G_LIST_MODEL (um->recent_faces), 0) != NULL)
+                g_list_store_remove (um->recent_faces, 0);
+
+        g_list_store_insert (um->recent_faces, 0,
+                             um->generated_avatar);
+        gtk_widget_show_all (um->recent_pictures);
+}
+
 UmPhotoDialog *
 um_photo_dialog_new (GtkWidget            *button,
                      SelectAvatarCallback  callback,
@@ -319,6 +355,7 @@ um_photo_dialog_class_init (UmPhotoDialogClass *klass)
         gtk_widget_class_set_template_from_resource (wclass, 
"/org/gnome/initial-setup/gis-account-avatar-chooser.ui");
 
         gtk_widget_class_bind_template_child (wclass, UmPhotoDialog, flowbox);
+        gtk_widget_class_bind_template_child (wclass, UmPhotoDialog, recent_pictures);
         gtk_widget_class_bind_template_child (wclass, UmPhotoDialog, take_picture_button);
 #ifdef HAVE_CHEESE
         gtk_widget_class_bind_template_callback (wclass, webcam_icon_selected);
diff --git a/gnome-initial-setup/pages/account/um-photo-dialog.h 
b/gnome-initial-setup/pages/account/um-photo-dialog.h
index 964b415..20a1e7d 100644
--- a/gnome-initial-setup/pages/account/um-photo-dialog.h
+++ b/gnome-initial-setup/pages/account/um-photo-dialog.h
@@ -41,6 +41,9 @@ UmPhotoDialog *um_photo_dialog_new      (GtkWidget            *button,
                                          gpointer              data);
 void           um_photo_dialog_free     (UmPhotoDialog *dialog);
 
+void           um_photo_dialog_generate_avatar (UmPhotoDialog *dialog,
+                                                const gchar   *name);
+
 G_END_DECLS
 
 #endif
diff --git a/gnome-initial-setup/pages/account/um-utils.c b/gnome-initial-setup/pages/account/um-utils.c
index 00133a7..678f1eb 100644
--- a/gnome-initial-setup/pages/account/um-utils.c
+++ b/gnome-initial-setup/pages/account/um-utils.c
@@ -476,3 +476,121 @@ generate_username_choices (const gchar  *name,
         g_string_free (item3, TRUE);
         g_string_free (item4, TRUE);
 }
+
+#define IMAGE_SIZE 512
+
+static gchar *
+extract_initials_from_name (const gchar *name)
+{
+        GString *initials = g_string_new ("");
+        const gchar *p;
+        gchar *normalized;
+        gunichar unichar;
+
+        p = g_strstrip (g_utf8_strup (name, -1));
+        normalized = g_utf8_normalize (p, -1, G_NORMALIZE_DEFAULT_COMPOSE);
+        if (normalized == NULL) {
+                g_free (normalized);
+
+                return NULL;
+        }
+
+        unichar = g_utf8_get_char (normalized);
+        g_string_append_unichar (initials, unichar);
+
+        p = g_utf8_strrchr (normalized, -1, ' ');
+        if (p != NULL && g_utf8_next_char (p) != NULL) {
+                p = g_utf8_next_char (p);
+
+                unichar = g_utf8_get_char (p);
+                g_string_append_unichar (initials, unichar);
+        }
+
+        g_free (normalized);
+
+        return g_string_free (initials, FALSE);
+}
+
+GdkRGBA
+get_color_for_name (const gchar *name)
+{
+        // https://gitlab.gnome.org/Community/Design/HIG-app-icons/blob/master/GNOME%20HIG.gpl
+        static gdouble gnome_color_palette[][3] = {
+                {  98, 160, 234 },
+                {  53, 132, 228 },
+                {  28, 113, 216 },
+                {  26,  95, 180 },
+                {  87, 227, 137 },
+                {  51, 209, 122 },
+                {  46, 194, 126 },
+                {  38, 162, 105 },
+                { 248, 228,  92 },
+                { 246, 211,  45 },
+                { 245, 194,  17 },
+                { 229, 165,  10 },
+                { 255, 163,  72 },
+                { 255, 120,   0 },
+                { 230,  97,   0 },
+                { 198,  70,   0 },
+                { 237,  51,  59 },
+                { 224,  27,  36 },
+                { 192,  28,  40 },
+                { 165,  29,  45 },
+                { 192,  97, 203 },
+                { 163,  71, 186 },
+                { 129,  61, 156 },
+                {  97,  53, 131 },
+                { 181, 131,  90 },
+                { 152, 106,  68 },
+                { 134,  94,  60 },
+                {  99,  69,  44 }
+        };
+
+        GdkRGBA color = { 255, 255, 255, 1.0 };
+        guint hash = g_str_hash (name);
+        gint number_of_colors = G_N_ELEMENTS (gnome_color_palette);
+        gint idx = hash % number_of_colors;
+
+        color.red   = gnome_color_palette[idx][0];
+        color.green = gnome_color_palette[idx][1];
+        color.blue  = gnome_color_palette[idx][2];
+
+        return color;
+}
+
+cairo_surface_t *
+generate_user_picture (const gchar *name) {
+        PangoFontDescription *font_desc;
+        g_autofree gchar *initials = extract_initials_from_name (name);
+        g_autofree gchar *font = g_strdup_printf ("Sans %d", (int)ceil (IMAGE_SIZE / 2.5));
+        PangoLayout *layout;
+        GdkRGBA color = get_color_for_name (name);
+        cairo_surface_t *surface;
+        gint width, height;
+        cairo_t *cr;
+
+        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                              IMAGE_SIZE,
+                                              IMAGE_SIZE);
+        cr = cairo_create (surface);
+
+        cairo_arc (cr, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2, 0, 2 * G_PI);
+        cairo_set_source_rgb (cr, color.red/255.0, color.green/255.0, color.blue/255.0);
+        cairo_fill (cr);
+
+        /* Draw the initials on top */
+        cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+        layout = pango_cairo_create_layout (cr);
+        pango_layout_set_text (layout, initials, -1);
+        font_desc = pango_font_description_from_string (font);
+        pango_layout_set_font_description (layout, font_desc);
+        pango_font_description_free (font_desc);
+
+        pango_layout_get_size (layout, &width, &height);
+        cairo_translate (cr, IMAGE_SIZE/2, IMAGE_SIZE/2);
+        cairo_move_to (cr, - ((double)width / PANGO_SCALE)/2, - ((double)height/PANGO_SCALE)/2);
+        pango_cairo_show_layout (cr, layout);
+        cairo_destroy (cr);
+
+        return surface;
+}
diff --git a/gnome-initial-setup/pages/account/um-utils.h b/gnome-initial-setup/pages/account/um-utils.h
index d298721..7a3c010 100644
--- a/gnome-initial-setup/pages/account/um-utils.h
+++ b/gnome-initial-setup/pages/account/um-utils.h
@@ -52,6 +52,9 @@ gboolean is_valid_username                (const gchar     *name,
 void     generate_username_choices        (const gchar     *name,
                                            GtkListStore    *store);
 
+cairo_surface_t *generate_user_picture    (const gchar     *name);
+
+
 G_END_DECLS
 
 #endif


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