Commit 8993f5fd authored by Julian Sparber's avatar Julian Sparber

avatar: refactor avatar loading and caching

* use the same method everywhere to display an avatar
* move code to generate letter avatars to a separate crate
* use the generated avatar as a fallback till the real avatar is
avaibile
* remove gtk dependecies from fractal-matrix-api

Note: The avatar in room details dialog is borken but the dialog will be
replaced soon. Also updating the username in the room history doesn't
work, the room history needs a refactor to resolve some issues.
parent 33fa0560
......@@ -381,9 +381,11 @@ dependencies = [
"gstreamer-player 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"gtk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"html2pango 0.1.0 (git+https://gitlab.gnome.org/World/html2pango)",
"letter-avatar 0.1.0 (git+https://gitlab.gnome.org/jsparber/letter-avatar)",
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"notify-rust 3.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"secret-service 0.4.0 (git+https://github.com/jhaye/secret-service-rs?rev=3c265527e43376fe8e00ddfa645a70813c35f449)",
......@@ -391,6 +393,7 @@ dependencies = [
"serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
"tree_magic 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -400,13 +403,9 @@ version = "3.29.1"
dependencies = [
"cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"md5 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
"reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -414,7 +413,6 @@ dependencies = [
"serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"tree_magic 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"urlencoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
......@@ -924,6 +922,17 @@ name = "lazycell"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "letter-avatar"
version = "0.1.0"
source = "git+https://gitlab.gnome.org/jsparber/letter-avatar#7a58d07442654e3e2dda9960e1ec3f14e2dba25a"
dependencies = [
"cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.40"
......@@ -2242,6 +2251,7 @@ dependencies = [
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef"
"checksum letter-avatar 0.1.0 (git+https://gitlab.gnome.org/jsparber/letter-avatar)" = "<none>"
"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b"
"checksum libflate 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1a429b86418868c7ea91ee50e9170683f47fd9d94f5375438ec86ec3adb74e8e"
"checksum linkify 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ce9439c6f4a1092dc1861272bef01034891da39f13aa1cdcf40ca3e4081de5f"
......
......@@ -16,6 +16,7 @@ gstreamer = "0.11.3"
gstreamer-player = "0.11.3"
notify-rust = "3.4.2"
pango = "0.4.0"
pangocairo = "0.5.0"
secret-service = "0.4.0"
serde = "1.0.43"
serde_derive = "1.0.43"
......@@ -25,6 +26,8 @@ rand = "0.4.2"
html2pango = { git = "https://gitlab.gnome.org/World/html2pango" }
comrak = "0.2.9"
gettext-rs = { git = "https://github.com/danigm/gettext-rs", branch = "no-gettext", features = ["gettext-system"] }
letter-avatar = { git = "https://gitlab.gnome.org/jsparber/letter-avatar" }
unicode-segmentation = "1.2.0"
regex = "1.0.0"
tree_magic = "0.2.0"
log = "0.4.2"
......
......@@ -9,6 +9,7 @@ use backend::BKCommand;
use widgets;
use widgets::AvatarExt;
use cache::download_to_cache;
use fractal_api::types::UserInfo;
impl AppOp {
......@@ -211,7 +212,7 @@ impl AppOp {
avatar_spinner.hide();
avatar_btn.set_sensitive(true);
self.show_avatar(self.avatar.clone());
self.show_avatar();
name_btn.hide();
name.set_editable(true);
......@@ -338,10 +339,10 @@ impl AppOp {
self.set_avatar(path.clone());
avatar_spinner.hide();
avatar_btn.set_sensitive(true);
self.show_avatar(self.avatar.clone());
self.show_avatar();
}
pub fn show_avatar(&self, path: Option<String>) {
pub fn show_avatar(&self) {
let stack = self.ui.builder
.get_object::<gtk::Stack>("account_settings_stack")
.expect("Can't find account_settings_delete_box in ui file.");
......@@ -358,7 +359,9 @@ impl AppOp {
}
}
let w = widgets::Avatar::circle_avatar(path.unwrap_or_default(), Some(100));
download_to_cache(self.backend.clone(), self.uid.clone().unwrap_or_default());
let w = widgets::Avatar::avatar_new(Some(100));
w.circle(self.uid.clone().unwrap_or_default(), self.username.clone(), 100);
avatar.add(&w);
/* FIXME: hack to make the avatar drawing area clickable*/
......@@ -380,7 +383,7 @@ impl AppOp {
self.backend.send(command).unwrap();
avatar_btn.set_sensitive(false);
avatar_spinner.show();
self.show_avatar(Some(file));
self.show_avatar();
}
pub fn show_new_username(&mut self, name: Option<String>) {
......
......@@ -44,7 +44,7 @@ impl AppOp {
body.truncate(80);
let (tx, rx): (Sender<(String, String)>, Receiver<(String, String)>) = channel();
self.backend.send(BKCommand::GetUserInfoAsync(msg.sender.clone(), tx)).unwrap();
self.backend.send(BKCommand::GetUserInfoAsync(msg.sender.clone(), Some(tx))).unwrap();
let bk = self.internal.clone();
let m = msg.clone();
gtk::timeout_add(50, move || match rx.try_recv() {
......
extern crate gtk;
extern crate gdk_pixbuf;
extern crate rand;
use i18n::{i18n, i18n_k};
......@@ -17,14 +16,12 @@ use backend::BKCommand;
use globals;
use cache;
use widgets;
use widgets::AvatarExt;
use types::Room;
use types::Message;
use util::markup_text;
use self::gdk_pixbuf::Pixbuf;
use self::rand::{thread_rng, Rng};
......@@ -419,7 +416,7 @@ impl AppOp {
};
}
pub fn set_current_room_avatar(&self, avatar: Option<String>, size: i32) {
pub fn set_current_room_avatar(&self, _avatar: Option<String>, _size: i32) {
let image = self.ui.builder
.get_object::<gtk::Box>("room_image")
.expect("Can't find room_image in ui file.");
......@@ -427,6 +424,8 @@ impl AppOp {
image.remove(&ch);
}
/*
* This will be removed soon.
let config = self.ui.builder
.get_object::<gtk::Image>("room_avatar_image")
.expect("Can't find room_avatar_image in ui file.");
......@@ -442,6 +441,7 @@ impl AppOp {
image.add(&w);
config.set_from_icon_name("camera-photo-symbolic", 1);
}
*/
}
pub fn filter_rooms(&self, term: Option<String>) {
......
......@@ -4,6 +4,8 @@ use self::gtk::prelude::*;
use appop::AppOp;
use cache::download_to_cache;
use backend::BKCommand;
use widgets;
use widgets::AvatarExt;
......@@ -41,7 +43,9 @@ impl AppOp {
avatar.remove(w);
}
let w = widgets::Avatar::circle_avatar(self.avatar.clone().unwrap_or_default(), Some(40));
download_to_cache(self.backend.clone(), self.uid.clone().unwrap_or_default());
let w = widgets::Avatar::avatar_new(Some(40));
w.circle(self.uid.clone().unwrap_or_default(), self.username.clone(), 40);
avatar.add(&w);
stack.set_visible_child_name("info");
}
......@@ -55,11 +59,13 @@ impl AppOp {
.expect("Can't find user_menu_button in ui file.");
let eb = gtk::EventBox::new();
match self.avatar.clone() {
Some(s) => {
let w = widgets::Avatar::circle_avatar(s.clone(), Some(24));
eb.add(&w);
}
match self.avatar.clone() {
Some(_) => {
download_to_cache(self.backend.clone(), self.uid.clone().unwrap_or_default());
let w = widgets::Avatar::avatar_new(Some(24));
w.circle(self.uid.clone().unwrap_or_default(), self.username.clone(), 24);
eb.add(&w);
}
None => {
let w = gtk::Spinner::new();
w.show();
......
......@@ -11,6 +11,10 @@ use error::Error;
use fractal_api::util::cache_path;
use globals;
/* includes for avatar download */
use backend::BKCommand;
use std::sync::mpsc::Sender;
use types::Message;
#[derive(Serialize, Deserialize)]
......@@ -71,3 +75,8 @@ pub fn destroy() -> Result<(), Error> {
let fname = cache_path("")?;
remove_dir_all(fname).or_else(|_| Err(Error::CacheError))
}
/* this downloads a avatar and stores it in the cache folder */
pub fn download_to_cache(backend: Sender<BKCommand>, name: String) {
let _ = backend.send(BKCommand::GetUserInfoAsync(name.clone(), None));
}
extern crate gtk;
extern crate gdk;
extern crate glib;
extern crate gdk_pixbuf;
extern crate cairo;
extern crate letter_avatar;
use std::cell::RefCell;
use std::rc::Rc;
use self::gtk::prelude::*;
pub use self::gtk::DrawingArea;
use self::gdk_pixbuf::Pixbuf;
use self::gdk_pixbuf::PixbufExt;
use self::gdk::ContextExt;
use fractal_api::util::cache_path;
pub type Avatar = gtk::Box;
pub trait AvatarExt {
fn avatar_new(size: Option<i32>) -> gtk::Box;
fn circle_avatar(path: String, size: Option<i32>) -> gtk::Box;
fn clean(&self);
fn create_da(&self, size: Option<i32>) -> DrawingArea;
fn circle(&self, path: String, size: Option<i32>);
fn default(&self, icon: String, size: Option<i32>);
fn circle(&self, uid: String, username: Option<String>, size: i32);
}
impl AvatarExt for gtk::Box {
......@@ -51,102 +53,72 @@ impl AvatarExt for gtk::Box {
b
}
fn circle_avatar(path: String, size: Option<i32>) -> gtk::Box {
let b = gtk::Box::new(gtk::Orientation::Horizontal, 0);
b.create_da(size);
b.circle(path, size);
b.show_all();
if let Some(style) = b.get_style_context() {
style.add_class("avatar");
fn circle(&self, uid: String, username: Option<String>, size: i32) {
struct Data {
cache : Result<Pixbuf, glib::Error>,
fallback: cairo::ImageSurface
}
b
}
fn default(&self, icon: String, size: Option<i32>) {
self.clean();
let da = self.create_da(size);
let s = size.unwrap_or(40);
let pixbuf = match gtk::IconTheme::get_default() {
None => None,
Some(i1) => match i1.load_icon(&icon[..], s, gtk::IconLookupFlags::empty()) {
Err(_) => None,
Ok(i2) => i2,
}
let da = self.create_da(Some(size));
let path = cache_path(&uid).unwrap_or(String::from(""));
let user_avatar = Pixbuf::new_from_file_at_scale(&path, size, -1, true);
/* remove IRC postfix from the username */
let username = if let Some(u) = username {
Some(u.trim_right_matches(" (IRC)").to_owned())
}else {
None
};
/* This function should never fail */
let fallback = letter_avatar::generate::new(uid, username, size as f64).unwrap();
da.connect_draw(move |da, g| {
use std::f64::consts::PI;
let width = s as f64;
let height = s as f64;
let context = da.get_style_context().unwrap();
gtk::render_background(&context, g, 0.0, 0.0, width, height);
if let Some(ref pb) = pixbuf {
let hpos: f64 = (width - (pb.get_height()) as f64) / 2.0;
g.arc(width / 2.0, height / 2.0, width.min(height) / 2.5, 0.0, 2.0 * PI);
g.clip();
g.set_source_pixbuf(&pb, 0.0, hpos);
g.rectangle(0.0, 0.0, width, height);
g.fill();
}
Inhibit(false)
});
}
fn circle(&self, path: String, size: Option<i32>) {
if path.starts_with("mxc:") {
self.default(String::from("image-loading-symbolic"), size);
return;
}
self.clean();
let da = self.create_da(size);
let s = size.unwrap_or(40);
let pixbuf = Pixbuf::new_from_file_at_scale(&path, s, -1, true);
let user_cache: Rc<RefCell<Data>> = Rc::new(RefCell::new(Data {cache: user_avatar, fallback: fallback}));
let user_cache = user_cache.clone();
da.connect_draw(move |da, g| {
use std::f64::consts::PI;
g.set_antialias(cairo::Antialias::Best);
let width = s as f64;
let height = s as f64;
let context = da.get_style_context().unwrap();
gtk::render_background(&context, g, 0.0, 0.0, width, height);
if let Ok(ref pb) = pixbuf {
let hpos: f64 = (width - (pb.get_height()) as f64) / 2.0;
let width = size as f64;
let height = size as f64;
g.arc(width / 2.0, height / 2.0, width.min(height) / 2.0, 0.0, 2.0 * PI);
g.clip();
g.set_antialias(cairo::Antialias::Best);
g.set_source_pixbuf(&pb, 0.0, hpos);
g.rectangle(0.0, 0.0, width, height);
g.fill();
{
let data = user_cache.borrow();
if let Ok(ref pb) = data.cache {
let context = da.get_style_context().unwrap();
gtk::render_background(&context, g, 0.0, 0.0, width, height);
g.arc(width / 2.0, height / 2.0, width.min(height) / 2.0, 0.0, 2.0 * PI);
g.clip();
let hpos: f64 = (width - (pb.get_height()) as f64) / 2.0;
g.set_source_pixbuf(&pb, 0.0, hpos);
} else {
/* use fallback */
g.set_source_surface(&data.fallback, 0f64, 0f64);
}
}
/* we should look into the cache only if the cache has changed, but this information is
* not yet available, we could also create our own signal and not use the draw signal*/
{
let mut data = user_cache.borrow_mut();
let new_avatar = Pixbuf::new_from_file_at_scale(&path, size, -1, true);
data.cache = new_avatar;
}
g.rectangle(0.0, 0.0, width, height);
g.fill();
Inhibit(false)
});
}
}
pub enum AdminColor {
Gold,
Silver,
}
pub fn admin_badge(kind: AdminColor, size: Option<i32>) -> gtk::DrawingArea {
let s = size.unwrap_or(10);
......
extern crate pango;
extern crate gtk;
extern crate gdk_pixbuf;
use self::gdk_pixbuf::Pixbuf;
use self::gtk::prelude::*;
use types::Member;
use backend::BKCommand;
use std::sync::mpsc::channel;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc::TryRecvError;
use appop::AppOp;
use globals;
use cache::download_to_cache;
use widgets;
use widgets::AvatarExt;
......@@ -61,10 +54,10 @@ impl<'a> MemberBox<'a> {
style.add_class("member");
}
download_to_cache(backend.clone(), self.member.uid.clone());
let avatar = widgets::Avatar::avatar_new(Some(globals::USERLIST_ICON_SIZE));
avatar.default(String::from("avatar-default-symbolic"),
Some(globals::USERLIST_ICON_SIZE));
get_member_info(backend.clone(), avatar.clone(), username.clone(), self.member.uid.clone(), globals::USERLIST_ICON_SIZE, 10);
avatar.circle(self.member.uid.clone(), Some(alias.clone()), globals::USERLIST_ICON_SIZE);
//get_member_info(backend.clone(), avatar.clone(), username.clone(), self.member.uid.clone(), globals::USERLIST_ICON_SIZE, 10);
avatar.set_margin_start(3);
avatar.set_valign(gtk::Align::Center);
......@@ -99,69 +92,3 @@ impl<'a> MemberBox<'a> {
event_box
}
}
#[allow(dead_code)]
pub fn get_member_avatar(backend: Sender<BKCommand>,
img: widgets::Avatar,
m: Option<Member>,
size: i32, tries: i32) {
if tries <= 0 {
return;
}
let (tx, rx): (Sender<String>, Receiver<String>) = channel();
backend.send(BKCommand::GetAvatarAsync(m.clone(), tx)).unwrap();
gtk::timeout_add(100, move || match rx.try_recv() {
Err(TryRecvError::Empty) => gtk::Continue(true),
Err(TryRecvError::Disconnected) => gtk::Continue(false),
Ok(avatar) => {
if let Ok(_) = Pixbuf::new_from_file_at_scale(&avatar, size, size, false) {
img.circle(avatar, Some(size));
} else {
// trying again if fail
img.default(String::from("avatar-default-symbolic"), Some(size));
get_member_avatar(backend.clone(), img.clone(), m.clone(), size, tries - 1);
}
gtk::Continue(false)
}
});
}
pub fn get_member_info(backend: Sender<BKCommand>,
img: widgets::Avatar,
username: gtk::Label,
sender: String,
size: i32, tries: i32) {
if tries <= 0 {
return;
}
let (tx, rx): (Sender<(String, String)>, Receiver<(String, String)>) = channel();
backend.send(BKCommand::GetUserInfoAsync(sender.clone(), tx)).unwrap();
gtk::timeout_add(100, move || match rx.try_recv() {
Err(TryRecvError::Empty) => gtk::Continue(true),
Err(TryRecvError::Disconnected) => gtk::Continue(false),
Ok((name, avatar)) => {
if let Ok(_) = Pixbuf::new_from_file_at_scale(&avatar, size, size, false) {
img.circle(avatar, Some(size));
} else {
// trying again if fail
img.default(String::from("avatar-default-symbolic"), Some(size));
get_member_info(backend.clone(), img.clone(), username.clone(), sender.clone(), size, tries - 1);
return gtk::Continue(false);
}
if !name.is_empty() {
username.set_text(&name);
} else {
get_member_info(backend.clone(), img.clone(), username.clone(), sender.clone(), size, tries - 1);
}
gtk::Continue(false)
}
});
}
......@@ -16,19 +16,18 @@ use self::chrono::prelude::*;
use backend::BKCommand;
use fractal_api as api;
use util::markup_text;
use std::path::Path;
use std::sync::mpsc::channel;
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc::TryRecvError;
use cache::download_to_cache;
use appop::AppOp;
use globals;
use widgets;
use widgets::AvatarExt;
use widgets::member::get_member_info;
// Room Message item
pub struct MessageBox<'a> {
......@@ -136,34 +135,25 @@ impl<'a> MessageBox<'a> {
}
fn build_room_msg_avatar(&self) -> widgets::Avatar {
let sender = self.msg.sender.clone();
let backend = self.op.backend.clone();
let uid = self.msg.sender.clone();
let avatar = widgets::Avatar::avatar_new(Some(globals::MSG_ICON_SIZE));
let fname = api::util::cache_path(&sender).unwrap_or(strn!(""));
let m = self.room.members.get(&uid);
let pathname = fname.clone();
let p = Path::new(&pathname);
if p.is_file() {
avatar.circle(fname, Some(globals::MSG_ICON_SIZE));
} else {
avatar.default(String::from("avatar-default-symbolic"),
Some(globals::MSG_ICON_SIZE));
}
let m = self.room.members.get(&sender);
match m {
let username = match m {
Some(member) => {
self.username.set_text(&member.get_alias());
get_member_info(backend.clone(), avatar.clone(), self.username.clone(), sender.clone(), globals::MSG_ICON_SIZE, 10);
Some(member.get_alias())
}
None => {
self.username.set_text(&sender);
get_member_info(backend.clone(), avatar.clone(), self.username.clone(), sender.clone(), globals::MSG_ICON_SIZE, 10);
self.username.set_text(&uid);
None
}
};
download_to_cache(self.op.backend.clone(), uid.clone());
avatar.circle(uid, username, globals::MSG_ICON_SIZE);
avatar
}
......
extern crate gtk;
extern crate gdk;
extern crate gdk_pixbuf;
extern crate cairo;
extern crate pango;
use i18n::i18n;
use self::gtk::prelude::*;
use self::gdk_pixbuf::Pixbuf;
use self::gdk_pixbuf::PixbufExt;
use self::gdk::ContextExt;
use fractal_api::util::AvatarMode;
use fractal_api::util::draw_identicon;
use types::Room;
use backend::BKCommand;
use util::markup_text;
use util::glib_thread_prelude::*;
use appop::AppOp;
use widgets::image::Image;
use widgets;
use widgets::AvatarExt;
use self::gtk::WidgetExt;
const AVATAR_SIZE: i32 = 60;
......@@ -48,19 +39,8 @@ impl<'a> RoomBox<'a> {
let widget_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
let mut avatar = gtk::DrawingArea::new();
if room.avatar.clone().unwrap_or_default().is_empty() {
make_identicon(&avatar, AVATAR_SIZE, room.id.clone(), room.name.clone().unwrap_or_default());
} else {
let mut avatar_widget = Image::new(&self.op.backend,
&room.avatar.clone().unwrap_or_default())
.size(Some((AVATAR_SIZE, AVATAR_SIZE)))
.thumb(true).circle(true)
.fixed(true).build();
avatar_widget.fixed_size = true;
avatar = avatar_widget.widget;
}
let avatar = widgets::Avatar::avatar_new(Some(AVATAR_SIZE));
avatar.circle(room.id.clone(), room.name.clone(), AVATAR_SIZE);
widget_box.pack_start(&avatar, false, false, 18);
let details_box = gtk::Box::new(gtk::Orientation::Vertical, 6);
......@@ -135,44 +115,3 @@ impl<'a> RoomBox<'a> {
widget_box
}
}
fn make_identicon(da: &gtk::DrawingArea, size: i32, rid: String, name: String) {
let da = da.clone();
glib_thread!(Result<String, Error>,
|| {
identicon!(&rid, name)
},
|rc: Result<String, Error>| {
if let Ok(path) = rc {
da.set_size_request(size, size);
let pixbuf = Pixbuf::new_from_file_at_scale(&path, size, size, true);
da.connect_draw(move |da, g| {
use std::f64::consts::PI;
g.set_antialias(cairo::Antialias::Best);
let width = size as f64;
let height = size as f64;
let context = da.get_style_context().unwrap();
gtk::render_background(&context, g, 0.0, 0.0, width, height);
if let Ok(ref pb) = pixbuf {
let hpos: f64 = (width - (pb.get_height()) as f64) / 2.0;
g.arc(width / 2.0, height / 2.0, width.min(height) / 2.0, 0.0, 2.0 * PI);
g.clip();
g.set_source_pixbuf(&pb, 0.0, hpos);
g.rectangle(0.0, 0.0, width, height);
g.fill();
}
Inhibit(false)
});
}
}
);
}
......@@ -162,7 +162,7 @@ impl RoomListGroup {
let rid = r.id.clone();
self.roomvec.lock().unwrap().push(RoomUpdated::new(r.clone()));
let row = RoomRow::new(r, &self.baseu);
let row = RoomRow::new(r);
self.list.add(&row.widget());
self.rooms.insert(rid, row);
......@@ -187,7 +187,7 @@ impl RoomListGroup {
rv.insert(pos, RoomUpdated::new(r.room.clone()));
let row = RoomRow::new(r.room, &self.baseu);
let row = RoomRow::new(r.room);