user-accounts: Generate a default avatar for new users
When a user is created, we generate an avatar with their initials. See https://gitlab.gnome.org/GNOME/Initiatives/issues/6
This commit is contained in:
parent
12ad6a1b18
commit
02c288ab6f
4 changed files with 187 additions and 40 deletions
|
@ -66,46 +66,6 @@ struct _CcAvatarChooser {
|
||||||
|
|
||||||
G_DEFINE_TYPE (CcAvatarChooser, cc_avatar_chooser, GTK_TYPE_POPOVER)
|
G_DEFINE_TYPE (CcAvatarChooser, cc_avatar_chooser, GTK_TYPE_POPOVER)
|
||||||
|
|
||||||
static void
|
|
||||||
set_user_icon_data (ActUser *user,
|
|
||||||
GdkPixbuf *pixbuf)
|
|
||||||
{
|
|
||||||
gchar *path;
|
|
||||||
gint fd;
|
|
||||||
GOutputStream *stream;
|
|
||||||
GError *error;
|
|
||||||
|
|
||||||
path = g_build_filename (g_get_tmp_dir (), "gnome-control-center-user-icon-XXXXXX", NULL);
|
|
||||||
fd = g_mkstemp (path);
|
|
||||||
|
|
||||||
if (fd == -1) {
|
|
||||||
g_warning ("failed to create temporary file for image data");
|
|
||||||
g_free (path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream = g_unix_output_stream_new (fd, TRUE);
|
|
||||||
|
|
||||||
error = NULL;
|
|
||||||
if (!gdk_pixbuf_save_to_stream (pixbuf, stream, "png", NULL, &error, NULL)) {
|
|
||||||
g_warning ("failed to save image: %s", error->message);
|
|
||||||
g_error_free (error);
|
|
||||||
g_object_unref (stream);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_unref (stream);
|
|
||||||
|
|
||||||
act_user_set_icon_file (user, path);
|
|
||||||
|
|
||||||
/* if we ever make the dbus call async, the g_remove call needs
|
|
||||||
* to wait for its completion
|
|
||||||
*/
|
|
||||||
g_remove (path);
|
|
||||||
|
|
||||||
g_free (path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
crop_dialog_response (GtkWidget *dialog,
|
crop_dialog_response (GtkWidget *dialog,
|
||||||
gint response_id,
|
gint response_id,
|
||||||
|
|
|
@ -354,6 +354,7 @@ static void
|
||||||
add_user (CcUserPanel *self)
|
add_user (CcUserPanel *self)
|
||||||
{
|
{
|
||||||
CcAddUserDialog *dialog;
|
CcAddUserDialog *dialog;
|
||||||
|
g_autoptr(GdkPixbuf) pixbuf = NULL;
|
||||||
GtkWindow *toplevel;
|
GtkWindow *toplevel;
|
||||||
ActUser *user;
|
ActUser *user;
|
||||||
|
|
||||||
|
@ -364,6 +365,7 @@ add_user (CcUserPanel *self)
|
||||||
gtk_dialog_run (GTK_DIALOG (dialog));
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
||||||
|
|
||||||
user = cc_add_user_dialog_get_user (dialog);
|
user = cc_add_user_dialog_get_user (dialog);
|
||||||
|
generate_user_avatar (user);
|
||||||
if (user != NULL)
|
if (user != NULL)
|
||||||
reload_users (self, user);
|
reload_users (self, user);
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,14 @@
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
|
#include <gio/gunixoutputstream.h>
|
||||||
#include <glib/gi18n.h>
|
#include <glib/gi18n.h>
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
|
||||||
#include "user-utils.h"
|
#include "user-utils.h"
|
||||||
|
|
||||||
|
#define IMAGE_SIZE 512
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gchar *text;
|
gchar *text;
|
||||||
gchar *placeholder_str;
|
gchar *placeholder_str;
|
||||||
|
@ -469,3 +473,179 @@ round_image (GdkPixbuf *pixbuf,
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
extract_initials_from_name (const gchar *name)
|
||||||
|
{
|
||||||
|
GString *initials;
|
||||||
|
g_autofree gchar *p = NULL;
|
||||||
|
g_autofree gchar *normalized = NULL;
|
||||||
|
gunichar unichar;
|
||||||
|
gpointer q = NULL;
|
||||||
|
|
||||||
|
p = g_utf8_strup (name, -1);
|
||||||
|
normalized = g_utf8_normalize (g_strstrip (p), -1, G_NORMALIZE_DEFAULT_COMPOSE);
|
||||||
|
if (normalized == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
initials = g_string_new ("");
|
||||||
|
|
||||||
|
unichar = g_utf8_get_char (normalized);
|
||||||
|
g_string_append_unichar (initials, unichar);
|
||||||
|
|
||||||
|
q = g_utf8_strrchr (normalized, -1, ' ');
|
||||||
|
if (q != NULL && g_utf8_next_char (q) != NULL) {
|
||||||
|
q = g_utf8_next_char (q);
|
||||||
|
|
||||||
|
unichar = g_utf8_get_char (q);
|
||||||
|
g_string_append_unichar (initials, unichar);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_string_free (initials, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
gint number_of_colors;
|
||||||
|
gint idx;
|
||||||
|
|
||||||
|
if (name == NULL || strlen (name) == 0)
|
||||||
|
return color;
|
||||||
|
|
||||||
|
hash = g_str_hash (name);
|
||||||
|
number_of_colors = G_N_ELEMENTS (gnome_color_palette);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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_rectangle (cr, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_user_icon_data (ActUser *user,
|
||||||
|
GdkPixbuf *pixbuf)
|
||||||
|
{
|
||||||
|
gchar *path;
|
||||||
|
gint fd;
|
||||||
|
GOutputStream *stream;
|
||||||
|
GError *error;
|
||||||
|
|
||||||
|
path = g_build_filename (g_get_tmp_dir (), "gnome-control-center-user-icon-XXXXXX", NULL);
|
||||||
|
fd = g_mkstemp (path);
|
||||||
|
|
||||||
|
if (fd == -1) {
|
||||||
|
g_warning ("failed to create temporary file for image data");
|
||||||
|
g_free (path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = g_unix_output_stream_new (fd, TRUE);
|
||||||
|
|
||||||
|
error = NULL;
|
||||||
|
if (!gdk_pixbuf_save_to_stream (pixbuf, stream, "png", NULL, &error, NULL)) {
|
||||||
|
g_warning ("failed to save image: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
g_object_unref (stream);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref (stream);
|
||||||
|
|
||||||
|
act_user_set_icon_file (user, path);
|
||||||
|
|
||||||
|
/* if we ever make the dbus call async, the g_remove call needs
|
||||||
|
* to wait for its completion
|
||||||
|
*/
|
||||||
|
g_remove (path);
|
||||||
|
|
||||||
|
g_free (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
generate_user_avatar (ActUser *user)
|
||||||
|
{
|
||||||
|
g_autoptr(GdkPixbuf) pixbuf = NULL;
|
||||||
|
cairo_surface_t *surface;
|
||||||
|
|
||||||
|
surface = generate_user_picture (act_user_get_real_name (user));
|
||||||
|
|
||||||
|
pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, IMAGE_SIZE, IMAGE_SIZE);
|
||||||
|
cairo_surface_destroy (surface);
|
||||||
|
|
||||||
|
set_user_icon_data (user, pixbuf);
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <act/act.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
@ -44,4 +45,8 @@ gboolean is_valid_username (const gchar *name,
|
||||||
gchar **tip);
|
gchar **tip);
|
||||||
GdkPixbuf *round_image (GdkPixbuf *pixbuf,
|
GdkPixbuf *round_image (GdkPixbuf *pixbuf,
|
||||||
gint icon_size);
|
gint icon_size);
|
||||||
|
void generate_user_avatar (ActUser *user);
|
||||||
|
void set_user_icon_data (ActUser *user,
|
||||||
|
GdkPixbuf *pixbuf);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue