[gnome-contacts] Avatar: cleaner UI.
- From: Niels De Graef <nielsdg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-contacts] Avatar: cleaner UI.
- Date: Fri, 19 Jan 2018 15:14:36 +0000 (UTC)
commit cc0f37a64a331beb7de71cdf1448a1708cf9eb13
Author: Niels De Graef <nielsdegraef gmail com>
Date: Fri Jan 19 16:05:07 2018 +0100
Avatar: cleaner UI.
* Always clip to a circle.
* In case there is no avatar, use a specific color for each contact and
draw a default icon on top if it.
data/ui/style.css | 2 +-
src/contacts-avatar.vala | 111 +++++++++++++++++++++--------
src/contacts-contact-list.vala | 4 +-
src/contacts-contact.vala | 95 -------------------------
src/contacts-link-suggestion-grid.vala | 3 +-
src/contacts-linked-accounts-dialog.vala | 4 +-
6 files changed, 89 insertions(+), 130 deletions(-)
---
diff --git a/data/ui/style.css b/data/ui/style.css
index ea89ca8..067c6f8 100644
--- a/data/ui/style.css
+++ b/data/ui/style.css
@@ -91,6 +91,6 @@ ContactsWindow .primary-toolbar.toolbar {
text-shadow: none;
}
-.contacts-avatar-dialog .contact-display-name {
+.contacts-avatar-popover .contact-display-name {
font-size: 20px;
}
diff --git a/src/contacts-avatar.vala b/src/contacts-avatar.vala
index 5b0d1d6..286ff95 100644
--- a/src/contacts-avatar.vala
+++ b/src/contacts-avatar.vala
@@ -26,7 +26,12 @@ using Gee;
public class Contacts.Avatar : DrawingArea {
private int size;
private Gdk.Pixbuf? pixbuf;
- private Pango.Layout? layout;
+ private Contact? contact;
+
+ // The background color used in case of a fallback avatar
+ private Gdk.RGBA? bg_color = null;
+ // The color used for an initial or the fallback icon
+ private const Gdk.RGBA fg_color = { 0, 0, 0, 0.25 };
public Avatar (int size) {
this.size = size;
@@ -37,25 +42,27 @@ public class Contacts.Avatar : DrawingArea {
show ();
}
- public void set_pixbuf (Gdk.Pixbuf a_pixbuf) {
- pixbuf = Contact.frame_icon (a_pixbuf);
+ public void set_pixbuf (Gdk.Pixbuf? a_pixbuf) {
+ this.pixbuf = a_pixbuf;
queue_draw ();
}
public void set_image (AvatarDetails? details, Contact? contact = null) {
+ this.contact = contact;
+
+ // FIXME listen for changes in the Individual's avatar
+
Gdk.Pixbuf? a_pixbuf = null;
- if (details != null &&
- details.avatar != null) {
+ if (details != null && details.avatar != null) {
try {
- var stream = details.avatar.load (size, null);
- a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true);
- }
- catch {
+ var stream = details.avatar.load (size, null);
+ a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true);
+ } catch {
}
}
if (a_pixbuf == null) {
- a_pixbuf = Contact.draw_fallback_avatar (size, contact);
+ a_pixbuf = null;
}
set_pixbuf (a_pixbuf);
}
@@ -63,30 +70,72 @@ public class Contacts.Avatar : DrawingArea {
public override bool draw (Cairo.Context cr) {
cr.save ();
- if (pixbuf != null) {
- Gdk.cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
- cr.paint();
- }
+ if (this.pixbuf != null)
+ draw_contact_avatar (cr);
+ else // No avatar available, draw a fallback
+ draw_fallback (cr);
- if (layout != null) {
- Utils.cairo_rounded_box (cr, 0, 0, size, size, 4);
- cr.clip ();
-
- cr.set_source_rgba (0, 0, 0, 0.5);
- cr.rectangle (0, size, size, 0);
- cr.fill ();
-
- cr.set_source_rgb (1.0, 1.0, 1.0);
- Pango.Rectangle rect;
- layout.get_extents (null, out rect);
- double label_width = rect.width/(double)Pango.SCALE;
- double label_height = rect.height / (double)Pango.SCALE;
- cr.move_to (Math.round ((size - label_width) / 2.0),
- size + Math.floor ((-label_height) / 2.0));
- Pango.cairo_show_layout (cr, layout);
- }
cr.restore ();
return true;
}
+
+ private void draw_contact_avatar (Cairo.Context cr) {
+ Gdk.cairo_set_source_pixbuf (cr, this.pixbuf, 0, 0);
+ // Clip with a circle
+ create_circle (cr);
+ cr.clip_preserve ();
+ cr.paint ();
+ }
+
+ private void draw_fallback (Cairo.Context cr) {
+ // The background color
+ if (this.bg_color == null)
+ calculate_color ();
+
+ // Fill the background circle
+ cr.set_source_rgb (this.bg_color.red, this.bg_color.green, this.bg_color.blue);
+ cr.arc (this.size / 2, this.size / 2, this.size / 2, 0, 2*Math.PI);
+ create_circle (cr);
+ cr.fill_preserve ();
+
+ // Draw the icon
+ try {
+ // FIXME we can probably cache this
+ var theme = IconTheme.get_default ();
+ var fallback_avatar = theme.lookup_icon ("avatar-default",
+ this.size * 4 / 5,
+ IconLookupFlags.FORCE_SYMBOLIC);
+ var icon_pixbuf = fallback_avatar.load_symbolic (fg_color);
+ create_circle (cr);
+ cr.clip_preserve ();
+ Gdk.cairo_set_source_pixbuf (cr, icon_pixbuf, 1 + this.size / 10, 1 + this.size / 5);
+ cr.paint ();
+ } catch (Error e) {
+ warning ("Couldn't get default avatar icon: %s", e.message);
+ }
+ }
+
+ private void calculate_color () {
+ //XXX find something if this.contact == nulll or id == ""
+
+ // We use the hash of the id so we get the same color for a contact
+ var hash = str_hash (this.contact.individual.id);
+
+ var r = ((hash & 0xFF0000) >> 16) / 255.0;
+ var g = ((hash & 0x00FF00) >> 8) / 255.0;
+ var b = (hash & 0x0000FF) / 255.0;
+
+ // Make it a bit lighter by default (and since the foreground will be darker)
+ this.bg_color = Gdk.RGBA () {
+ red = (r + 2) / 3.0,
+ green = (g + 2) / 3.0,
+ blue = (b + 2) / 3.0,
+ alpha = 0
+ };
+ }
+
+ private void create_circle (Cairo.Context cr) {
+ cr.arc (this.size / 2, this.size / 2, this.size / 2, 0, 2*Math.PI);
+ }
}
diff --git a/src/contacts-contact-list.vala b/src/contacts-contact-list.vala
index bc8868e..6bc2a25 100644
--- a/src/contacts-contact-list.vala
+++ b/src/contacts-contact-list.vala
@@ -26,6 +26,8 @@ using Gee;
*/
public class Contacts.ContactList : ListBox {
private class ContactDataRow : ListBoxRow {
+ private const int LIST_AVATAR_SIZE = 48;
+
public Contact contact;
public Label label;
private Avatar avatar;
@@ -41,7 +43,7 @@ public class Contacts.ContactList : ListBox {
grid.margin = 3;
grid.margin_start = 9;
grid.set_column_spacing (10);
- this.avatar = new Avatar (Contact.LIST_AVATAR_SIZE);
+ this.avatar = new Avatar (LIST_AVATAR_SIZE);
label = new Label ("");
label.set_ellipsize (Pango.EllipsizeMode.END);
diff --git a/src/contacts-contact.vala b/src/contacts-contact.vala
index 08dcc12..82d2f6e 100644
--- a/src/contacts-contact.vala
+++ b/src/contacts-contact.vala
@@ -1,4 +1,3 @@
-/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
/*
* Copyright (C) 2011 Alexander Larsson <alexl redhat com>
*
@@ -26,9 +25,6 @@ public errordomain ContactError {
}
public class Contacts.Contact : GLib.Object {
- public const int LIST_AVATAR_SIZE = 48;
- public const int SMALL_AVATAR_SIZE = 54;
-
public weak Store store;
public bool is_main;
@@ -38,19 +34,6 @@ public class Contacts.Contact : GLib.Object {
public Persona? fake_persona = null;
- private Gdk.Pixbuf? _small_avatar;
- public Gdk.Pixbuf small_avatar {
- get {
- if (_small_avatar == null) {
- var pixbuf = load_icon (individual.avatar, SMALL_AVATAR_SIZE);
- if (pixbuf == null)
- pixbuf = draw_fallback_avatar (SMALL_AVATAR_SIZE, this);
- _small_avatar = frame_icon (pixbuf);
- }
- return _small_avatar;
- }
- }
-
public string display_name {
get { return this.individual.display_name; }
}
@@ -171,7 +154,6 @@ public class Contacts.Contact : GLib.Object {
individual.notify.disconnect(notify_cb);
individual = new_individual;
individual.set_data ("contact", this);
- _small_avatar = null;
individual.notify.connect(notify_cb);
queue_changed (true);
}
@@ -379,9 +361,6 @@ public class Contacts.Contact : GLib.Object {
}
private void notify_cb (ParamSpec pspec) {
- if (pspec.get_name () == "avatar") {
- _small_avatar = null;
- }
queue_changed (false);
}
@@ -430,80 +409,6 @@ public class Contacts.Contact : GLib.Object {
update_filter_data ();
}
- // TODO: This should be async, but the vala bindings are broken (bug #649875)
- private Gdk.Pixbuf load_icon (LoadableIcon ?file, int size) {
- Gdk.Pixbuf? res = null;
- if (file != null) {
- try {
- Cancellable c = new Cancellable ();
- var stream = file.load (size, null, c);
- res = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true, c);
- } catch (GLib.Error e) {
- warning ("error loading avatar %s\n", e.message);
- }
- }
-
- return res;
- }
-
- public static Gdk.Pixbuf frame_icon (Gdk.Pixbuf icon) {
- int w = icon.get_width ();
- int h = icon.get_height ();
- var cst = new Cairo.ImageSurface (Cairo.Format.ARGB32, w, h);
- var cr = new Cairo.Context (cst);
-
- cr.set_source_rgba (0, 0, 0, 0);
- cr.rectangle (0, 0, w, h);
- cr.fill ();
-
- Gdk.cairo_set_source_pixbuf (cr, icon, 0, 0);
- Utils.cairo_rounded_box (cr,
- 0, 0,
- w, h, 4);
- cr.fill ();
-
- return Gdk.pixbuf_get_from_surface (cst, 0, 0, w, h);
- }
-
- private static Gdk.Pixbuf? fallback_pixbuf_default;
- public static Gdk.Pixbuf draw_fallback_avatar (int size, Contact? contact) {
- if (size == SMALL_AVATAR_SIZE && fallback_pixbuf_default != null)
- return fallback_pixbuf_default;
-
- Gdk.Pixbuf pixbuf = null;
- try {
- var cst = new Cairo.ImageSurface (Cairo.Format.ARGB32, size, size);
- var cr = new Cairo.Context (cst);
-
- var pat = new Cairo.Pattern.linear (0, 0, 0, size);
- pat.add_color_stop_rgb (0, 0.937, 0.937, 0.937);
- pat.add_color_stop_rgb (1, 0.969, 0.969, 0.969);
-
- cr.set_source (pat);
- cr.paint ();
-
- int avatar_size = (int) (size * 0.3);
- var icon_info = IconTheme.get_default ().lookup_icon ("avatar-default-symbolic", avatar_size,
- IconLookupFlags.GENERIC_FALLBACK);
- if (icon_info != null) {
- Gdk.cairo_set_source_pixbuf (cr, icon_info.load_icon (), (size - avatar_size) / 2, (size -
avatar_size) / 2);
- cr.rectangle ((size - avatar_size) / 2, (size - avatar_size) / 2, avatar_size, avatar_size);
- cr.fill ();
- }
- pixbuf = Gdk.pixbuf_get_from_surface (cst, 0, 0, size, size);
- } catch {
- }
-
- if (size == SMALL_AVATAR_SIZE)
- fallback_pixbuf_default = pixbuf;
-
- if (pixbuf != null)
- return pixbuf;
-
- var cst = new Cairo.ImageSurface (Cairo.Format.ARGB32, size, size);
- return Gdk.pixbuf_get_from_surface (cst, 0, 0, size, size);
- }
-
/* We claim something is "removable" if at least one persona is removable,
that will typically unlink the rest. */
public bool can_remove_personas () {
diff --git a/src/contacts-link-suggestion-grid.vala b/src/contacts-link-suggestion-grid.vala
index 03994ae..88cc551 100644
--- a/src/contacts-link-suggestion-grid.vala
+++ b/src/contacts-link-suggestion-grid.vala
@@ -26,6 +26,7 @@ using Gee;
*/
[GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-link-suggestion-grid.ui")]
public class Contacts.LinkSuggestionGrid : Grid {
+ private const int AVATAR_SIZE = 54;
[GtkChild]
private Gtk.Label description_label;
@@ -42,7 +43,7 @@ public class Contacts.LinkSuggestionGrid : Grid {
public LinkSuggestionGrid (Contact contact) {
get_style_context ().add_class ("contacts-suggestion");
- var image_frame = new Avatar (Contact.SMALL_AVATAR_SIZE);
+ var image_frame = new Avatar (AVATAR_SIZE);
image_frame.hexpand = false;
image_frame.margin = 12;
contact.keep_widget_uptodate (image_frame, (w) => {
diff --git a/src/contacts-linked-accounts-dialog.vala b/src/contacts-linked-accounts-dialog.vala
index 95a69b4..b6b377f 100644
--- a/src/contacts-linked-accounts-dialog.vala
+++ b/src/contacts-linked-accounts-dialog.vala
@@ -20,6 +20,8 @@ using Gtk;
using Folks;
public class Contacts.LinkedAccountsDialog : Dialog {
+ private const int AVATAR_SIZE = 54;
+
Contact contact;
ListBox linked_accounts_view;
@@ -78,7 +80,7 @@ public class Contacts.LinkedAccountsDialog : Dialog {
var row_grid = new Grid ();
- var image_frame = new Avatar (Contact.SMALL_AVATAR_SIZE);
+ var image_frame = new Avatar (AVATAR_SIZE);
image_frame.set_hexpand (false);
image_frame.margin = 6;
image_frame.margin_end = 12;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]