[fractal/wip/christopherdavis/use-hdyavatar] avatar: Use HdyAvatar instead of a custom DrawingArea




commit 75f6948da514c6fd548ea588d17c424eb874c99d
Author: Christopher Davis <brainblasted disroot org>
Date:   Mon Sep 28 23:23:09 2020 -0700

    avatar: Use HdyAvatar instead of a custom DrawingArea
    
    HdyAvatar allows for simple HiDPI image loading and
    a fallback image.
    
    Related to https://gitlab.gnome.org/GNOME/fractal/issues/496

 Cargo.lock                         |  50 ++--------------
 fractal-gtk/Cargo.toml             |   3 +-
 fractal-gtk/src/cache/mod.rs       |   6 +-
 fractal-gtk/src/widgets/avatar.rs  | 113 +++++++++----------------------------
 fractal-gtk/src/widgets/message.rs |   1 +
 5 files changed, 37 insertions(+), 136 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 0873b493..f8a849da 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -567,7 +567,6 @@ dependencies = [
  "html2pango",
  "itertools 0.8.2",
  "lazy_static",
- "letter-avatar",
  "libhandy",
  "log",
  "loggerv",
@@ -1422,18 +1421,6 @@ version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
 checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
-[[package]]
-name = "letter-avatar"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "662660bd131a9c06b36730750de8049efb5d2c8e0c23b3b58e9dd87e61acc245"
-dependencies = [
- "cairo-rs",
- "pango",
- "pangocairo",
- "unicode-segmentation",
-]
-
 [[package]]
 name = "libc"
 version = "0.2.76"
@@ -1442,12 +1429,14 @@ checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
 
 [[package]]
 name = "libhandy"
-version = "0.7.0"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "ce64d9c1f9e9444fb1175cf65cb6db9b55e7ae5f9a44140202da05784a7cdf33"
+checksum = "d776bf5b92993b8006688652cda2683261317dcc82bb4d91d3996bda2f7019e0"
 dependencies = [
  "bitflags",
  "gdk",
+ "gdk-pixbuf",
+ "gdk-pixbuf-sys",
  "gdk-sys",
  "gio",
  "gio-sys",
@@ -1882,37 +1871,6 @@ dependencies = [
  "system-deps",
 ]
 
-[[package]]
-name = "pangocairo"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "00f5ae67a05a5e023f09f64e9a71c845274d4b82dedee237b70425811885e883"
-dependencies = [
- "bitflags",
- "cairo-rs",
- "cairo-sys-rs",
- "glib",
- "glib-sys",
- "gobject-sys",
- "libc",
- "pango",
- "pango-sys",
- "pangocairo-sys",
-]
-
-[[package]]
-name = "pangocairo-sys"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "94ccc97f698c2f0233b84e5ca676893a1e676785b60eec700b9c0e6dcd0feb98"
-dependencies = [
- "cairo-sys-rs",
- "glib-sys",
- "libc",
- "pango-sys",
- "system-deps",
-]
-
 [[package]]
 name = "paste"
 version = "0.1.18"
diff --git a/fractal-gtk/Cargo.toml b/fractal-gtk/Cargo.toml
index 04b8b794..8ab99f2a 100644
--- a/fractal-gtk/Cargo.toml
+++ b/fractal-gtk/Cargo.toml
@@ -28,7 +28,6 @@ rand = "0.7.3"
 regex = "1.3.4"
 secret-service = "1.0.0"
 serde_json = "1.0.48"
-letter-avatar = "1.3.0"
 sourceview4 = "0.2.0"
 gspell = "0.5.0"
 gtk-sys = "0.10.0"
@@ -54,7 +53,7 @@ path = "../fractal-matrix-api"
 package = "fractal-matrix-api"
 
 [dependencies.libhandy]
-version = "0.7.0"
+version = "0.7.1"
 
 [dependencies.gettext-rs]
 git = "https://github.com/danigm/gettext-rs";
diff --git a/fractal-gtk/src/cache/mod.rs b/fractal-gtk/src/cache/mod.rs
index 8943eac6..8a8dc78a 100644
--- a/fractal-gtk/src/cache/mod.rs
+++ b/fractal-gtk/src/cache/mod.rs
@@ -178,7 +178,7 @@ pub fn download_to_cache(
         Err(TryRecvError::Empty) => Continue(true),
         Err(TryRecvError::Disconnected) => Continue(false),
         Ok(_resp) => {
-            data.borrow_mut().redraw_pixbuf();
+            data.borrow_mut().redraw(None);
             Continue(false)
         }
     });
@@ -204,7 +204,7 @@ pub fn download_to_cache_username(
             label.set_text(&username);
             if let Some(ref rc_data) = avatar {
                 let mut data = rc_data.borrow_mut();
-                data.redraw_fallback(Some(username));
+                data.redraw(Some(username));
             }
 
             Continue(false)
@@ -235,7 +235,7 @@ pub fn download_to_cache_username_emote(
             label.set_markup(&format!("<b>{}</b> {}", &username, text));
             if let Some(ref rc_data) = avatar {
                 let mut data = rc_data.borrow_mut();
-                data.redraw_fallback(Some(username));
+                data.redraw(Some(username));
             }
 
             Continue(false)
diff --git a/fractal-gtk/src/widgets/avatar.rs b/fractal-gtk/src/widgets/avatar.rs
index f2a2a725..1a764613 100644
--- a/fractal-gtk/src/widgets/avatar.rs
+++ b/fractal-gtk/src/widgets/avatar.rs
@@ -1,12 +1,12 @@
+use std::boxed::Box;
 use std::cell::RefCell;
 use std::path::Path;
 use std::rc::Rc;
 
 use crate::util::cache_dir_path;
-use gdk::prelude::GdkContextExt;
 use gdk_pixbuf::Pixbuf;
 use gtk::prelude::*;
-pub use gtk::DrawingArea;
+use libhandy::AvatarExt as HdyAvatarExt;
 
 pub enum AvatarBadgeColor {
     Gold,
@@ -18,25 +18,16 @@ pub type Avatar = gtk::Overlay;
 
 pub struct AvatarData {
     id: String,
-    username: Option<String>,
     size: i32,
     cache: Option<Pixbuf>,
-    pub widget: gtk::DrawingArea,
-    fallback: cairo::ImageSurface,
+    pub widget: libhandy::Avatar,
 }
 
 impl AvatarData {
-    pub fn redraw_fallback(&mut self, username: Option<String>) {
-        self.username = username.clone();
-        /* This function should never fail */
-        self.fallback = letter_avatar::generate::new(self.id.clone(), username, self.size as f64)
-            .expect("this function should never fail");
-        self.widget.queue_draw();
-    }
-
-    pub fn redraw_pixbuf(&mut self) {
+    pub fn redraw(&mut self, username: Option<String>) {
         let path = cache_dir_path(None, self.id.as_str()).unwrap_or_default();
         self.cache = load_pixbuf(&path, self.size);
+        username.map(|n| self.widget.set_text(Some(&n)));
         self.widget.queue_draw();
     }
 }
@@ -44,7 +35,7 @@ impl AvatarData {
 pub trait AvatarExt {
     fn avatar_new(size: Option<i32>) -> gtk::Overlay;
     fn clean(&self);
-    fn create_da(&self, size: Option<i32>) -> DrawingArea;
+    fn create_avatar(&self, size: Option<i32>) -> libhandy::Avatar;
     fn circle(
         &self,
         id: String,
@@ -62,20 +53,19 @@ impl AvatarExt for gtk::Overlay {
         }
     }
 
-    fn create_da(&self, size: Option<i32>) -> DrawingArea {
-        let da = DrawingArea::new();
-
+    fn create_avatar(&self, size: Option<i32>) -> libhandy::Avatar {
         let s = size.unwrap_or(40);
-        da.set_size_request(s, s);
-        self.add(&da);
+        let avatar = libhandy::Avatar::new(s, None, true);
+        avatar.set_show_initials(true);
+        self.add(&avatar);
         self.show_all();
 
-        da
+        avatar
     }
 
     fn avatar_new(size: Option<i32>) -> gtk::Overlay {
         let b = gtk::Overlay::new();
-        b.create_da(size);
+        b.create_avatar(size);
         b.show_all();
         b.get_style_context().add_class("avatar");
 
@@ -96,21 +86,21 @@ impl AvatarExt for gtk::Overlay {
         badge_size: Option<i32>,
     ) -> Rc<RefCell<AvatarData>> {
         self.clean();
-        let da = self.create_da(Some(size));
+        let avatar = self.create_avatar(Some(size));
         let path = cache_dir_path(None, id.as_str()).unwrap_or_default();
-        let user_avatar = load_pixbuf(&path, size);
-        let uname = username.clone();
+        let cache = load_pixbuf(&path, size);
         /* remove IRC postfix from the username */
         let username = if let Some(u) = username {
-            Some(u.trim_end_matches(" (IRC)").to_owned())
+            u.trim_end_matches(" (IRC)")
+                .trim_start_matches("#")
+                .to_owned()
         } else {
-            None
+            id.clone()
         };
-        /* This function should never fail */
-        let fallback = letter_avatar::generate::new(id.clone(), username, size as f64)
-            .expect("this function should never fail");
+
+        avatar.set_text(Some(&username));
+
         // Power level badge setup
-        let has_badge = badge_color.is_some();
         let badge_size = badge_size.unwrap_or(size / 3);
         if let Some(color) = badge_color {
             let badge = gtk::Box::new(gtk::Orientation::Vertical, 0);
@@ -127,64 +117,17 @@ impl AvatarExt for gtk::Overlay {
         }
 
         let data = AvatarData {
-            id,
-            username: uname,
+            id: id.clone(),
             size,
-            cache: user_avatar,
-            fallback,
-            widget: da.clone(),
+            cache,
+            widget: avatar.clone(),
         };
         let avatar_cache: Rc<RefCell<AvatarData>> = Rc::new(RefCell::new(data));
 
-        let user_cache = avatar_cache.clone();
-        da.connect_draw(move |da, g| {
-            use std::f64::consts::PI;
-            let width = size as f64;
-            let height = size as f64;
-
-            g.set_antialias(cairo::Antialias::Best);
-
-            {
-                g.set_fill_rule(cairo::FillRule::EvenOdd);
-                g.arc(
-                    width / 2.0,
-                    height / 2.0,
-                    width.min(height) / 2.0,
-                    0.0,
-                    2.0 * PI,
-                );
-                if has_badge {
-                    g.clip_preserve();
-                    g.new_sub_path();
-                    let badge_radius = badge_size as f64 / 2.0;
-                    g.arc(
-                        width - badge_radius,
-                        badge_radius,
-                        badge_radius * 1.4,
-                        0.0,
-                        2.0 * PI,
-                    );
-                }
-                g.clip();
-
-                let data = user_cache.borrow();
-                if let Some(ref pb) = data.cache {
-                    let context = da.get_style_context();
-                    gtk::render_background(&context, g, 0.0, 0.0, width, height);
-
-                    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);
-                }
-            }
-
-            g.rectangle(0.0, 0.0, width, height);
-            g.fill();
-
-            Inhibit(false)
-        });
+        avatar.set_image_load_func(Some(Box::new(move |sz| {
+            let path = cache_dir_path(None, id.as_str()).unwrap_or_default();
+            load_pixbuf(&path, sz)
+        })));
 
         avatar_cache
     }
diff --git a/fractal-gtk/src/widgets/message.rs b/fractal-gtk/src/widgets/message.rs
index 084e16e9..d68e98ec 100644
--- a/fractal-gtk/src/widgets/message.rs
+++ b/fractal-gtk/src/widgets/message.rs
@@ -268,6 +268,7 @@ impl MessageBox {
         let uid = msg.sender.clone();
         let alias = msg.sender_name.clone();
         let avatar = widgets::Avatar::avatar_new(Some(globals::MSG_ICON_SIZE));
+        avatar.set_valign(gtk::Align::Start);
 
         let data = avatar.circle(
             uid.to_string(),


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]