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 @@
using Gtk;
using Folks;
using Hdy;
/**
* The AvatarSelector can be used to choose the avatar for a contact.
......@@ -109,16 +110,12 @@ public class Contacts.AvatarSelector : Popover {
}
}
private FlowBoxChild create_thumbnail (Gdk.Pixbuf source_pixbuf) {
var avatar = new Avatar (ICONS_SIZE);
var pixbuf = source_pixbuf.scale_simple (ICONS_SIZE, ICONS_SIZE, Gdk.InterpType.HYPER);
avatar.set_pixbuf (pixbuf);
private FlowBoxChild create_button (Avatar avatar) {
var button = new Button ();
button.get_style_context ().add_class (AVATAR_BUTTON_CSS_NAME);
button.image = avatar;
button.clicked.connect ( () => {
selected_pixbuf (scale_pixbuf_for_avatar_use (source_pixbuf));
selected_pixbuf (avatar.get_pixbuf (MAIN_SIZE));
this.popdown ();
});
var child = new FlowBoxChild ();
......@@ -133,24 +130,13 @@ public class Contacts.AvatarSelector : Popover {
if (details == null || details.avatar == null)
return null;
try {
var stream = details.avatar.load (MAIN_SIZE, null);
return create_thumbnail (new Gdk.Pixbuf.from_stream (stream));
} catch {
debug ("Couldn't create frame for persona \"%s\".", persona.display_id);
}
return null;
var avatar = new Avatar.for_persona (ICONS_SIZE, persona);
return create_button (avatar);
}
private FlowBoxChild? thumbnail_for_filename (string filename) {
try {
return create_thumbnail (new Gdk.Pixbuf.from_file (filename));
} catch {
debug ("Couldn't create frame for file \"%s\".", filename);
}
return null;
var avatar = new Avatar.for_file (ICONS_SIZE, filename);
return create_button (avatar);
}
private void update_thumbnail_grids () {
......@@ -255,8 +241,8 @@ public class Contacts.AvatarSelector : Popover {
} catch {
}
if (chooser is Dialog)
((Dialog) chooser).set_response_sensitive (ResponseType.ACCEPT, (pixbuf != null));
if (chooser is Gtk.Dialog)
((Gtk.Dialog) chooser).set_response_sensitive (ResponseType.ACCEPT, (pixbuf != null));
if (pixbuf != null)
preview.set_from_pixbuf (pixbuf);
......
......@@ -16,6 +16,7 @@
*/
using Gtk;
using Hdy;
using Folks;
using Gee;
......@@ -23,98 +24,102 @@ using Gee;
* The Avatar of a Contact is responsible for showing an {@link Folks.Individual}'s
* avatar, or a fallback if it's not available.
*/
public class Contacts.Avatar : DrawingArea {
private int size;
private Gdk.Pixbuf? pixbuf = null;
private Gdk.Pixbuf? cache = null;
public class Contacts.Avatar : Hdy.Avatar {
private Individual? individual = null;
// We want to lazily load the Pixbuf to make sure we don't draw all contact avatars at once.
// As long as there is no need for it to be drawn, keep this to false.
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 ();
});
}
private Persona? persona = null;
private string? filename = null;
public Avatar.for_file (int size, string filename) {
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
this.avatar_loaded = (individual == null || individual.avatar == null);
public Avatar.for_persona (int size, Persona persona) {
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);
}
/**
* Manually set the avatar to the given pixbuf, even if the contact has an avatar.
*/
public void set_pixbuf (Gdk.Pixbuf? a_pixbuf) {
this.cache = null;
this.pixbuf = a_pixbuf;
queue_draw ();
public Avatar.for_individual (int size, Individual individual) {
string name = "";
this.individual = individual;
individual.notify["avatar"].connect ( (s, p) => {
this.set_image_load_func (load_from_avatar_details);
print ("avatar changed\n");
});
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 () {
assert (this.individual != null);
private Gdk.Pixbuf load_from_file (int size) {
Gdk.Pixbuf pixbuf = null;
int width, height;
if (filename == null)
return null;
this.avatar_loaded = true;
try {
var stream = yield this.individual.avatar.load_async (this.size);
this.cache = null;
this.pixbuf = yield new Gdk.Pixbuf.from_stream_at_scale_async (stream, this.size, this.size, true);
queue_draw ();
Gdk.Pixbuf.get_file_info (this.filename, out width, out height);
pixbuf = new Gdk.Pixbuf.from_file_at_scale (this.filename,
(width <= height) ? size : -1,
(width >= height) ? size : -1,
true);
} 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) {
// This exists to implement lazy loading: i.e. only load the avatar on the first draw()
if (!this.avatar_loaded)
load_avatar.begin ();
if (this.cache != null) {
// Don't do anything if we have already a cached avatar
} else if (this.pixbuf != null)
this.cache = create_contact_avatar ();
else // No avatar or cache available, create the fallback
this.cache = create_fallback ();
private Gdk.Pixbuf load_from_avatar_details (int size) {
Gdk.Pixbuf pixbuf = null;
AvatarDetails details = null;
if (this.individual != null) {
details = this.individual as AvatarDetails;
} else if (this.persona != null) {
details = this.persona as AvatarDetails;
}
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) {
Gdk.cairo_set_source_pixbuf (cr, this.cache, 0, 0);
cr.paint ();
}
public Gdk.Pixbuf get_pixbuf (int size) {
Gdk.Pixbuf pixbuf = null;
private Gdk.Pixbuf create_contact_avatar () {
return AvatarUtils.round_image(this.pixbuf);
}
pixbuf = load_from_file (size);
private Gdk.Pixbuf create_fallback () {
string name = "";
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);
if (pixbuf == null)
pixbuf = load_from_avatar_details (size);
return pixbuf;
}
......@@ -126,6 +131,13 @@ public class Contacts.Avatar : DrawingArea {
private string find_display_name () {
string name = "";
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) {
if (p.store.is_primary_store) {
primary_persona = p;
......
......@@ -46,7 +46,7 @@ public class Contacts.ContactEditor : Box {
// Creates the contact's current avatar in a big button on top of the Editor
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 ();
button.get_accessible ().set_name (_("Change avatar"));
......
......@@ -45,7 +45,7 @@ public class Contacts.ContactList : ListBox {
grid.margin = 3;
grid.margin_start = 9;
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.ellipsize = Pango.EllipsizeMode.END;
......
......@@ -107,7 +107,7 @@ public class Contacts.ContactSheet : Grid {
this.last_row = 0;
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_valign (Align.START);
......
......@@ -43,7 +43,7 @@ public class Contacts.LinkSuggestionGrid : Grid {
public LinkSuggestionGrid (Individual individual) {
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.margin = 12;
image_frame.show ();
......
......@@ -50,7 +50,7 @@ public class Contacts.LinkedPersonasDialog : Dialog {
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.margin = 6;
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