Unverified Commit 0042f749 authored by Julian Sparber's avatar Julian Sparber
Browse files

Use HdyAvatar as avatar widget


Signed-off-by: Julian Sparber's avatarJulian Sparber <julian@sparber.net>
parent 443352c5
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
using Gtk; using Gtk;
using Folks; using Folks;
using Hdy;
/** /**
* The AvatarSelector can be used to choose the avatar for a contact. * The AvatarSelector can be used to choose the avatar for a contact.
...@@ -109,16 +110,12 @@ public class Contacts.AvatarSelector : Popover { ...@@ -109,16 +110,12 @@ public class Contacts.AvatarSelector : Popover {
} }
} }
private FlowBoxChild create_thumbnail (Gdk.Pixbuf source_pixbuf) { private FlowBoxChild create_button (Avatar avatar) {
var avatar = new Avatar (ICONS_SIZE);
var pixbuf = source_pixbuf.scale_simple (ICONS_SIZE, ICONS_SIZE, Gdk.InterpType.HYPER);
avatar.set_pixbuf (pixbuf);
var button = new Button (); var button = new Button ();
button.get_style_context ().add_class (AVATAR_BUTTON_CSS_NAME); button.get_style_context ().add_class (AVATAR_BUTTON_CSS_NAME);
button.image = avatar; button.image = avatar;
button.clicked.connect ( () => { button.clicked.connect ( () => {
selected_pixbuf (scale_pixbuf_for_avatar_use (source_pixbuf)); selected_pixbuf (avatar.get_pixbuf (MAIN_SIZE));
this.popdown (); this.popdown ();
}); });
var child = new FlowBoxChild (); var child = new FlowBoxChild ();
...@@ -133,24 +130,13 @@ public class Contacts.AvatarSelector : Popover { ...@@ -133,24 +130,13 @@ public class Contacts.AvatarSelector : Popover {
if (details == null || details.avatar == null) if (details == null || details.avatar == null)
return null; return null;
try { var avatar = new Avatar.for_persona (ICONS_SIZE, persona);
var stream = details.avatar.load (MAIN_SIZE, null); return create_button (avatar);
return create_thumbnail (new Gdk.Pixbuf.from_stream (stream));
} catch {
debug ("Couldn't create frame for persona \"%s\".", persona.display_id);
}
return null;
} }
private FlowBoxChild? thumbnail_for_filename (string filename) { private FlowBoxChild? thumbnail_for_filename (string filename) {
try { var avatar = new Avatar.for_file (ICONS_SIZE, filename);
return create_thumbnail (new Gdk.Pixbuf.from_file (filename)); return create_button (avatar);
} catch {
debug ("Couldn't create frame for file \"%s\".", filename);
}
return null;
} }
private void update_thumbnail_grids () { private void update_thumbnail_grids () {
...@@ -255,8 +241,8 @@ public class Contacts.AvatarSelector : Popover { ...@@ -255,8 +241,8 @@ public class Contacts.AvatarSelector : Popover {
} catch { } catch {
} }
if (chooser is Dialog) if (chooser is Gtk.Dialog)
((Dialog) chooser).set_response_sensitive (ResponseType.ACCEPT, (pixbuf != null)); ((Gtk.Dialog) chooser).set_response_sensitive (ResponseType.ACCEPT, (pixbuf != null));
if (pixbuf != null) if (pixbuf != null)
preview.set_from_pixbuf (pixbuf); preview.set_from_pixbuf (pixbuf);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
*/ */
using Gtk; using Gtk;
using Hdy;
using Folks; using Folks;
using Gee; using Gee;
...@@ -23,98 +24,102 @@ using Gee; ...@@ -23,98 +24,102 @@ using Gee;
* The Avatar of a Contact is responsible for showing an {@link Folks.Individual}'s * The Avatar of a Contact is responsible for showing an {@link Folks.Individual}'s
* avatar, or a fallback if it's not available. * avatar, or a fallback if it's not available.
*/ */
public class Contacts.Avatar : DrawingArea { public class Contacts.Avatar : Hdy.Avatar {
private int size;
private Gdk.Pixbuf? pixbuf = null;
private Gdk.Pixbuf? cache = null;
private Individual? individual = null; private Individual? individual = null;
// We want to lazily load the Pixbuf to make sure we don't draw all contact avatars at once. private Persona? persona = null;
// As long as there is no need for it to be drawn, keep this to false. private string? filename = null;
private bool avatar_loaded = false;
public Avatar (int size, Individual? individual = null) {
this.individual = individual;
if (individual != null) {
individual.notify["avatar"].connect ( (s, p) => {
load_avatar.begin ();
});
}
public Avatar.for_file (int size, string filename) {
this.size = size; this.size = size;
set_size_request (size, size); this.filename = filename;
this.set_image_load_func (load_from_file);
}
// If we don't have an avatar, don't try to load it later public Avatar.for_persona (int size, Persona persona) {
this.avatar_loaded = (individual == null || individual.avatar == null); this.persona = persona;
persona.notify["avatar"].connect ( (s, p) => {
this.set_image_load_func (load_from_avatar_details);
});
show (); this.text = find_display_name ();
this.size = size;
this.set_image_load_func (load_from_avatar_details);
} }
/** public Avatar.for_individual (int size, Individual individual) {
* Manually set the avatar to the given pixbuf, even if the contact has an avatar. string name = "";
*/ this.individual = individual;
public void set_pixbuf (Gdk.Pixbuf? a_pixbuf) { individual.notify["avatar"].connect ( (s, p) => {
this.cache = null; this.set_image_load_func (load_from_avatar_details);
this.pixbuf = a_pixbuf; print ("avatar changed\n");
queue_draw (); });
name = find_display_name ();
/* If we don't have a usable name use the display_name
* to generate the color but don't show any label
*/
if (name == "") {
this.text = individual.display_name;
this.show_initials = false;
} else {
this.show_initials = true;
this.text = name;
}
this.size = size;
this.set_image_load_func (load_from_avatar_details);
} }
private async void load_avatar () { private Gdk.Pixbuf load_from_file (int size) {
assert (this.individual != null); Gdk.Pixbuf pixbuf = null;
int width, height;
if (filename == null)
return null;
this.avatar_loaded = true;
try { try {
var stream = yield this.individual.avatar.load_async (this.size); Gdk.Pixbuf.get_file_info (this.filename, out width, out height);
this.cache = null; pixbuf = new Gdk.Pixbuf.from_file_at_scale (this.filename,
this.pixbuf = yield new Gdk.Pixbuf.from_stream_at_scale_async (stream, this.size, this.size, true); (width <= height) ? size : -1,
queue_draw (); (width >= height) ? size : -1,
true);
} catch (Error e) { } catch (Error e) {
debug ("Couldn't load avatar of contact %s. Reason: %s", this.individual.display_name, e.message); debug ("Couldn't create avatar for file \"%s\".", filename);
} }
return pixbuf;
} }
public override bool draw (Cairo.Context cr) { private Gdk.Pixbuf load_from_avatar_details (int size) {
// This exists to implement lazy loading: i.e. only load the avatar on the first draw() Gdk.Pixbuf pixbuf = null;
if (!this.avatar_loaded) AvatarDetails details = null;
load_avatar.begin (); if (this.individual != null) {
details = this.individual as AvatarDetails;
if (this.cache != null) { } else if (this.persona != null) {
// Don't do anything if we have already a cached avatar details = this.persona as AvatarDetails;
} else if (this.pixbuf != null) }
this.cache = create_contact_avatar ();
else // No avatar or cache available, create the fallback
this.cache = create_fallback ();
draw_cached_avatar (cr); if (details != null && details.avatar != null) {
try {
/* TODO: Probably we need to scale the avater in a nice way to
* address issues with non square avatars. */
var stream = details.avatar.load (size, null, null);
pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true);
} catch (Error e) {
debug ("Couldn't load avatar of contact %s. Reason: %s", this.individual.display_name, e.message);
}
}
return true; return pixbuf;
} }
private void draw_cached_avatar (Cairo.Context cr) { public Gdk.Pixbuf get_pixbuf (int size) {
Gdk.cairo_set_source_pixbuf (cr, this.cache, 0, 0); Gdk.Pixbuf pixbuf = null;
cr.paint ();
}
private Gdk.Pixbuf create_contact_avatar () { pixbuf = load_from_file (size);
return AvatarUtils.round_image(this.pixbuf);
}
private Gdk.Pixbuf create_fallback () { if (pixbuf == null)
string name = ""; pixbuf = load_from_avatar_details (size);
bool show_label = false;
if (this.individual != null) {
name = find_display_name ();
/* If we don't have a usable name use the display_name
* to generate the color but don't show any label
*/
if (name == "") {
name = this.individual.display_name;
} else {
show_label = true;
}
}
var pixbuf = AvatarUtils.generate_user_picture(name, this.size, show_label);
pixbuf = AvatarUtils.round_image(pixbuf);
return pixbuf; return pixbuf;
} }
...@@ -126,6 +131,13 @@ public class Contacts.Avatar : DrawingArea { ...@@ -126,6 +131,13 @@ public class Contacts.Avatar : DrawingArea {
private string find_display_name () { private string find_display_name () {
string name = ""; string name = "";
Persona primary_persona = null; Persona primary_persona = null;
if (this.persona != null) {
name = look_up_alias_for_display_name (this.persona);
if (name == "")
name = look_up_name_details_for_display_name (this.persona);
return name;
}
foreach (var p in this.individual.personas) { foreach (var p in this.individual.personas) {
if (p.store.is_primary_store) { if (p.store.is_primary_store) {
primary_persona = p; primary_persona = p;
......
...@@ -46,7 +46,7 @@ public class Contacts.ContactEditor : Box { ...@@ -46,7 +46,7 @@ public class Contacts.ContactEditor : Box {
// Creates the contact's current avatar in a big button on top of the Editor // Creates the contact's current avatar in a big button on top of the Editor
private Widget create_avatar_button () { private Widget create_avatar_button () {
this.avatar = new Avatar (PROFILE_SIZE, this.individual); this.avatar = new Avatar.for_individual (PROFILE_SIZE, this.individual);
var button = new Button (); var button = new Button ();
button.get_accessible ().set_name (_("Change avatar")); button.get_accessible ().set_name (_("Change avatar"));
......
...@@ -45,7 +45,7 @@ public class Contacts.ContactList : ListBox { ...@@ -45,7 +45,7 @@ public class Contacts.ContactList : ListBox {
grid.margin = 3; grid.margin = 3;
grid.margin_start = 9; grid.margin_start = 9;
grid.set_column_spacing (10); grid.set_column_spacing (10);
this.avatar = new Avatar (LIST_AVATAR_SIZE, this.individual); this.avatar = new Avatar.for_individual (LIST_AVATAR_SIZE, this.individual);
this.label = new Label (individual.display_name); this.label = new Label (individual.display_name);
this.label.ellipsize = Pango.EllipsizeMode.END; this.label.ellipsize = Pango.EllipsizeMode.END;
......
...@@ -107,7 +107,7 @@ public class Contacts.ContactSheet : Grid { ...@@ -107,7 +107,7 @@ public class Contacts.ContactSheet : Grid {
this.last_row = 0; this.last_row = 0;
this.foreach ((child) => this.remove (child)); this.foreach ((child) => this.remove (child));
var image_frame = new Avatar (PROFILE_SIZE, this.individual); var image_frame = new Avatar.for_individual (PROFILE_SIZE, this.individual);
image_frame.set_vexpand (false); image_frame.set_vexpand (false);
image_frame.set_valign (Align.START); image_frame.set_valign (Align.START);
......
...@@ -43,7 +43,7 @@ public class Contacts.LinkSuggestionGrid : Grid { ...@@ -43,7 +43,7 @@ public class Contacts.LinkSuggestionGrid : Grid {
public LinkSuggestionGrid (Individual individual) { public LinkSuggestionGrid (Individual individual) {
get_style_context ().add_class ("contacts-suggestion"); get_style_context ().add_class ("contacts-suggestion");
var image_frame = new Avatar (AVATAR_SIZE, individual); var image_frame = new Avatar.for_individual (AVATAR_SIZE, individual);
image_frame.hexpand = false; image_frame.hexpand = false;
image_frame.margin = 12; image_frame.margin = 12;
image_frame.show (); image_frame.show ();
......
...@@ -50,7 +50,7 @@ public class Contacts.LinkedPersonasDialog : Dialog { ...@@ -50,7 +50,7 @@ public class Contacts.LinkedPersonasDialog : Dialog {
var row_grid = new Grid (); var row_grid = new Grid ();
var image_frame = new Avatar (AVATAR_SIZE, individual); var image_frame = new Avatar.for_individual (AVATAR_SIZE, individual);
image_frame.set_hexpand (false); image_frame.set_hexpand (false);
image_frame.margin = 6; image_frame.margin = 6;
image_frame.margin_end = 12; image_frame.margin_end = 12;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment