[gnome-contacts/wip/nielsdg/avatar-popover] WIP: Avatars refresh.



commit 8df316138a8c5fcceca55b1e166216cb560fc8be
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Thu Jan 4 19:04:10 2018 +0100

    WIP: Avatars refresh.

 data/ui/contacts-avatar-dialog.ui |  313 +++++++++++--------------------------
 data/ui/style.css                 |   18 +--
 meson.build                       |    1 -
 src/contacts-avatar-dialog.vala   |  114 +++++---------
 src/contacts-contact-editor.vala  |   32 ++--
 src/contacts-contact-frame.vala   |  120 +++++++++++----
 src/contacts-utils.vala           |   22 ---
 src/meson.build                   |    2 -
 8 files changed, 239 insertions(+), 383 deletions(-)
---
diff --git a/data/ui/contacts-avatar-dialog.ui b/data/ui/contacts-avatar-dialog.ui
index b9c0819..851116b 100644
--- a/data/ui/contacts-avatar-dialog.ui
+++ b/data/ui/contacts-avatar-dialog.ui
@@ -1,293 +1,164 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.20"/>
-  <template class="ContactsAvatarDialog" parent="GtkDialog">
+  <template class="ContactsAvatarPopover" parent="GtkPopover">
     <property name="visible">True</property>
-    <property name="title" translatable="yes">Select Picture</property>
-    <property name="modal">True</property>
     <style>
-      <class name="contacts-avatar-dialog"/>
+      <class name="contacts-avatar-popover"/>
     </style>
-    <child internal-child="vbox">
+    <child>
       <object class="GtkBox">
         <property name="visible">True</property>
         <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <property name="margin">12</property>
         <child>
-          <object class="GtkGrid" id="grid">
+          <object class="GtkStack" id="views_stack">
             <property name="visible">True</property>
-            <property name="border_width">8</property>
-            <property name="column_spacing">16</property>
-            <property name="row_spacing">11</property>
             <child>
-            </child>
-              <!-- ContactFrame -->
-              <placeholder/>
-            <child>
-              <object class="GtkLabel" id="contact_name_label">
+              <object class="GtkFlowBox" id="personas_thumbnail_grid">
                 <property name="visible">True</property>
-                <property name="halign">start</property>
-                <property name="valign">start</property>
-                <property name="hexpand">True</property>
-                <property name="margin_top">4</property>
-                <property name="ellipsize">end</property>
-                <property name="label" translatable="yes">New Contact</property>
-                <style>
-                  <class name="contact-display-name"/>
-                </style>
+                <property name="orientation">vertical</property>
               </object>
               <packing>
-                <property name="top_attach">0</property>
-                <property name="left_attach">1</property>
+                <property name="name">thumbnail-page</property>
               </packing>
             </child>
             <child>
-              <object class="GtkFrame">
+              <object class="GtkGrid" id="crop_page">
                 <property name="visible">True</property>
-                <style>
-                  <class name="contacts-avatar-frame"/>
-                </style>
+                <property name="orientation">vertical</property>
                 <child>
-                  <object class="GtkStack" id="views_stack">
+                  <object class="GtkActionBar">
                     <property name="visible">True</property>
                     <child>
-                      <object class="GtkGrid" id="thumbnail_page">
+                      <object class="GtkBox">
                         <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
+                        <property name="orientation">horizontal</property>
+                        <style>
+                          <class name="linked"/>
+                        </style>
                         <child>
-                          <object class="GtkScrolledWindow">
+                          <object class="GtkButton">
                             <property name="visible">True</property>
-                            <property name="hscrollbar_policy">never</property>
-                            <property name="vscrollbar_policy">automatic</property>
-                            <property name="hexpand">True</property>
-                            <property name="vexpand">True</property>
-                            <property name="height_request">300</property>
+                            <signal name="clicked" handler="on_crop_page_select_button_clicked" 
swapped="no"/>
                             <child>
-                              <object class="GtkBox">
+                              <object class="GtkImage">
                                 <property name="visible">True</property>
-                                <property name="orientation">vertical</property>
-                                <child>
-                                  <object class="GtkFlowBox" id="personas_thumbnail_grid">
-                                    <property name="visible">True</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkSeparator">
-                                    <property name="visible">True</property>
-                                    <property name="orientation">horizontal</property>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkFlowBox" id="stock_thumbnail_grid">
-                                    <property name="visible">True</property>
-                                    <property name="min_children_per_line">5</property>
-                                    <property name="max_children_per_line">8</property>
-                                  </object>
-                                </child>
+                                <property name="can_focus">False</property>
+                                <property name="pixel_size">16</property>
+                                <property name="icon_name">object-select-symbolic</property>
                               </object>
                             </child>
                           </object>
                         </child>
                         <child>
-                          <object class="GtkActionBar">
+                          <object class="GtkButton">
                             <property name="visible">True</property>
+                            <signal name="clicked" handler="on_crop_page_cancel_button_clicked" 
swapped="no"/>
                             <child>
-                              <object class="GtkBox" id="webcam_button_box">
-                                <property name="orientation">horizontal</property>
-                                <style>
-                                  <class name="linked"/>
-                                </style>
-                                <child>
-                                  <object class="GtkButton">
-                                    <property name="visible">True</property>
-                                    <signal name="clicked" handler="select_avatar_file_cb" swapped="no"/>
-                                    <child>
-                                      <object class="GtkImage">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="pixel_size">16</property>
-                                        <property name="icon_name">list-add-symbolic</property>
-                                      </object>
-                                    </child>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkButton" id="webcam_button">
-                                    <property name="visible">True</property>
-                                    <property name="sensitive">False</property>
-                                    <signal name="clicked" handler="on_webcam_button_clicked" swapped="no"/>
-                                    <child>
-                                      <object class="GtkImage">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="pixel_size">16</property>
-                                        <property name="icon_name">camera-photo-symbolic</property>
-                                      </object>
-                                    </child>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkButton">
-                                <property name="visible" bind-source="webcam_button_box" 
bind-property="visible" bind-flags="invert-boolean|sync-create" />
-                                <signal name="clicked" handler="select_avatar_file_cb" swapped="no"/>
-                                <child>
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="pixel_size">16</property>
-                                    <property name="icon_name">list-add-symbolic</property>
-                                  </object>
-                                </child>
+                              <object class="GtkImage">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="pixel_size">16</property>
+                                <property name="icon_name">edit-undo-symbolic</property>
                               </object>
                             </child>
                           </object>
                         </child>
                       </object>
-                      <packing>
-                        <property name="name">thumbnail-page</property>
-                      </packing>
                     </child>
+                  </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="left_attach">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="name">crop-page</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkGrid" id="photobooth_page">
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkActionBar">
+                    <property name="visible">True</property>
                     <child>
-                      <object class="GtkGrid" id="crop_page">
+                      <object class="GtkBox">
                         <property name="visible">True</property>
-                        <property name="orientation">vertical</property>
+                        <property name="orientation">horizontal</property>
+                        <style>
+                          <class name="linked"/>
+                        </style>
                         <child>
-                          <object class="GtkActionBar">
+                          <object class="GtkButton">
                             <property name="visible">True</property>
+                            <signal name="clicked" handler="on_photobooth_page_select_button_clicked" 
swapped="no"/>
                             <child>
-                              <object class="GtkBox">
+                              <object class="GtkImage">
                                 <property name="visible">True</property>
-                                <property name="orientation">horizontal</property>
-                                <style>
-                                  <class name="linked"/>
-                                </style>
-                                <child>
-                                  <object class="GtkButton">
-                                    <property name="visible">True</property>
-                                    <signal name="clicked" handler="on_crop_page_select_button_clicked" 
swapped="no"/>
-                                    <child>
-                                      <object class="GtkImage">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="pixel_size">16</property>
-                                        <property name="icon_name">object-select-symbolic</property>
-                                      </object>
-                                    </child>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkButton">
-                                    <property name="visible">True</property>
-                                    <signal name="clicked" handler="on_crop_page_cancel_button_clicked" 
swapped="no"/>
-                                    <child>
-                                      <object class="GtkImage">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="pixel_size">16</property>
-                                        <property name="icon_name">edit-undo-symbolic</property>
-                                      </object>
-                                    </child>
-                                  </object>
-                                </child>
+                                <property name="can_focus">False</property>
+                                <property name="pixel_size">16</property>
+                                <property name="icon_name">object-select-symbolic</property>
                               </object>
                             </child>
                           </object>
-                          <packing>
-                            <property name="top_attach">1</property>
-                            <property name="left_attach">0</property>
-                          </packing>
                         </child>
-                      </object>
-                      <packing>
-                        <property name="name">crop-page</property>
-                      </packing>
-                    </child>
-                    <child>
-                      <object class="GtkGrid" id="photobooth_page">
-                        <property name="orientation">vertical</property>
                         <child>
-                          <object class="GtkActionBar">
+                          <object class="GtkButton">
                             <property name="visible">True</property>
+                            <signal name="clicked" handler="on_photobooth_page_cancel_button_clicked" 
swapped="no"/>
                             <child>
-                              <object class="GtkBox">
+                              <object class="GtkImage">
                                 <property name="visible">True</property>
-                                <property name="orientation">horizontal</property>
-                                <style>
-                                  <class name="linked"/>
-                                </style>
-                                <child>
-                                  <object class="GtkButton">
-                                    <property name="visible">True</property>
-                                    <signal name="clicked" 
handler="on_photobooth_page_select_button_clicked" swapped="no"/>
-                                    <child>
-                                      <object class="GtkImage">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="pixel_size">16</property>
-                                        <property name="icon_name">object-select-symbolic</property>
-                                      </object>
-                                    </child>
-                                  </object>
-                                </child>
-                                <child>
-                                  <object class="GtkButton">
-                                    <property name="visible">True</property>
-                                    <signal name="clicked" 
handler="on_photobooth_page_cancel_button_clicked" swapped="no"/>
-                                    <child>
-                                      <object class="GtkImage">
-                                        <property name="visible">True</property>
-                                        <property name="can_focus">False</property>
-                                        <property name="pixel_size">16</property>
-                                        <property name="icon_name">edit-undo-symbolic</property>
-                                      </object>
-                                    </child>
-                                  </object>
-                                </child>
+                                <property name="can_focus">False</property>
+                                <property name="pixel_size">16</property>
+                                <property name="icon_name">edit-undo-symbolic</property>
                               </object>
                             </child>
                           </object>
-                          <packing>
-                            <property name="top_attach">1</property>
-                            <property name="left_attach">0</property>
-                          </packing>
                         </child>
                       </object>
-                      <packing>
-                        <property name="name">photobooth-page</property>
-                      </packing>
                     </child>
                   </object>
+                  <packing>
+                    <property name="top_attach">1</property>
+                    <property name="left_attach">0</property>
+                  </packing>
                 </child>
               </object>
               <packing>
-                <property name="top_attach">1</property>
-                <property name="left_attach">0</property>
-                <property name="width">2</property>
+                <property name="name">photobooth-page</property>
               </packing>
             </child>
           </object>
         </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="orientation">horizontal</property>
+            <property name="halign">center</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkButton" id="webcam_button">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label" translatable="yes">Take a picture…</property>
+                <signal name="clicked" handler="on_webcam_button_clicked" swapped="no"/>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Select a file…</property>
+                <signal name="clicked" handler="select_avatar_file_cb" swapped="no"/>
+              </object>
+            </child>
+          </object>
+        </child>
       </object>
     </child>
-
-    <child type="action">
-      <object class="GtkButton" id="select_button">
-        <property name="visible">True</property>
-        <property name="can-default">True</property>
-        <property name="sensitive">False</property>
-        <property name="label" translatable="yes">Select</property>
-      </object>
-    </child>
-    <child type="action">
-      <object class="GtkButton" id="cancel_button">
-        <property name="visible">True</property>
-        <property name="label" translatable="yes">Cancel</property>
-      </object>
-    </child>
-    <action-widgets>
-      <action-widget response="cancel">cancel_button</action-widget>
-      <action-widget response="ok" default="true">select_button</action-widget>
-    </action-widgets>
   </template>
 </interface>
diff --git a/data/ui/style.css b/data/ui/style.css
index 6a75863..01c94bb 100644
--- a/data/ui/style.css
+++ b/data/ui/style.css
@@ -56,23 +56,11 @@ row.contact-data-row {
 }
 
 .contacts-avatar-frame.frame {
- border-width: 1px 1px 1px 1px;
- border-style: solid;
- border-color: @borders;
- border-image: none;
- border-radius: 0;
- padding: 0;
-}
-
-.main-avatar-frame.frame {
- border-width: 1px;
- border-style: solid;
- border-color: @borders;
- border-radius: 6px;
+  padding: 0;
 }
 
 .main-avatar-frame border {
- border-radius: 5px;
+  border-width: 0;
 }
 
 /* Give the avatar in the ContactSheet some margin,
@@ -136,6 +124,6 @@ ContactsWindow .primary-toolbar.toolbar {
   text-shadow: 1px 1px alpha(@theme_base_color, 0.6);
 }
 
-.contacts-avatar-dialog .contact-display-name {
+.contacts-avatar-popover .contact-display-name {
   font-size: 20px;
 }
diff --git a/meson.build b/meson.build
index 9238512..4f58e34 100644
--- a/meson.build
+++ b/meson.build
@@ -36,7 +36,6 @@ gee = dependency('gee-0.8')
 gio_unix = dependency('gio-unix-2.0', version: '>=' + min_glib_version)
 glib = dependency('glib-2.0', version: '>=' + min_glib_version)
 gmodule_export = dependency('gmodule-export-2.0', version: '>=' + min_glib_version)
-gnome_desktop = dependency('gnome-desktop-3.0')
 goa = dependency('goa-1.0')
 gtk = dependency('gtk+-3.0', version: '>= 3.22.0')
 libebook = dependency('libebook-1.2', version: '>=' + min_eds_version)
diff --git a/src/contacts-avatar-dialog.vala b/src/contacts-avatar-dialog.vala
index 58f3010..21a0031 100644
--- a/src/contacts-avatar-dialog.vala
+++ b/src/contacts-avatar-dialog.vala
@@ -21,40 +21,32 @@ using Folks;
 
 /**
  * The AvatarDialog can be used to choose the avatar for a contact.
- * This can be done by either choosing a stock thumbnail, an image file
- * provided by the user, or -if cheese is enabled- by using a webcam.
+ * This can be done by choosing from:
+ * - one of the contact's avatar,
+ * - an image file on the user's machine
+ * - (if cheese is enabled) a webcam.
+ * - a fallback avatar
  *
  * After a user has initially chosen an avatar, we provide a cropping tool.
  */
 [GtkTemplate (ui = "/org/gnome/Contacts/ui/contacts-avatar-dialog.ui")]
-public class Contacts.AvatarDialog : Dialog {
+public class Contacts.AvatarPopover : Popover {
   const int MAIN_SIZE = 128;
   const int ICONS_SIZE = 64;
 
   private Contact contact;
 
-  // This will provide the default thumbnails
-  private Gnome.DesktopThumbnailFactory thumbnail_factory;
-
-  [GtkChild]
-  private Grid grid;
-  [GtkChild]
-  private Label contact_name_label;
   [GtkChild]
   private Stack views_stack;
   [GtkChild]
   private FlowBox personas_thumbnail_grid;
   [GtkChild]
-  private FlowBox stock_thumbnail_grid;
-  [GtkChild]
   private Grid crop_page;
   private Cc.CropArea crop_area;
   [GtkChild]
   private Grid photobooth_page;
   [GtkChild]
   private Button webcam_button;
-  [GtkChild]
-  private Box webcam_button_box;
 
   private ContactFrame current_avatar;
 
@@ -72,13 +64,12 @@ public class Contacts.AvatarDialog : Dialog {
    */
   public signal void set_avatar (GLib.Icon avatar_icon);
 
-  public AvatarDialog (Window main_window, Contact? contact) {
+  public AvatarPopover (Widget? relative_to, Contact? contact) {
     Object (
-      transient_for: main_window,
-      use_header_bar: 1
+      relative_to: relative_to,
+      modal: true
     );
 
-    this.thumbnail_factory = new Gnome.DesktopThumbnailFactory (Gnome.ThumbnailSize.NORMAL);
     this.contact = contact;
 
     // Load the current avatar
@@ -92,14 +83,10 @@ public class Contacts.AvatarDialog : Dialog {
     }
     this.current_avatar.set_hexpand (false);
     this.current_avatar.show ();
-    this.grid.attach (this.current_avatar, 0, 0);
+    /* this.grid.attach (this.current_avatar, 0, 0); */
 
-    if (contact != null)
-      this.contact_name_label.label = contact.display_name;
 
 #if HAVE_CHEESE
-    this.webcam_button_box.show ();
-
     // Look for camera devices.
     this.camera_monitor = new Cheese.CameraDeviceMonitor ();
     this.camera_monitor.added.connect ( () => {
@@ -125,9 +112,6 @@ public class Contacts.AvatarDialog : Dialog {
     this.photobooth_page.show ();
 
     this.flash = new Cheese.Flash (this);
-#else
-    // Don't show the camera button
-    this.webcam_button_box.hide ();
 #endif
 
     this.views_stack.set_visible_child_name ("thumbnail-page");
@@ -141,7 +125,7 @@ public class Contacts.AvatarDialog : Dialog {
        });
     */
 
-    update_thumbnail_grids ();
+    update_thumbnail_grid ();
   }
 
   private Gdk.Pixbuf scale_pixbuf_for_avatar_use (Gdk.Pixbuf pixbuf) {
@@ -189,25 +173,14 @@ public class Contacts.AvatarDialog : Dialog {
     return null;
   }
 
-  private ContactFrame? frame_for_filename (string filename) {
-    ContactFrame? image_frame = null;
-    try {
-      var pixbuf = new Gdk.Pixbuf.from_file (filename);
-      return create_frame (pixbuf);
-    } catch {
-    }
-    return image_frame;
-  }
-
   private void selected_pixbuf (Gdk.Pixbuf pixbuf) {
     var p = pixbuf.scale_simple (MAIN_SIZE, MAIN_SIZE, Gdk.InterpType.HYPER);
     this.current_avatar.set_pixbuf (p);
 
     this.new_pixbuf = pixbuf;
-    set_response_sensitive (ResponseType.OK, true);
   }
 
-  private void update_thumbnail_grids () {
+  private void update_thumbnail_grid () {
     if (this.contact != null) {
       foreach (var p in contact.individual.personas) {
         ContactFrame? frame = frame_for_persona (p);
@@ -216,14 +189,6 @@ public class Contacts.AvatarDialog : Dialog {
       }
     }
     this.personas_thumbnail_grid.show_all ();
-
-    var stock_files = Utils.get_stock_avatars ();
-    foreach (var file_name in stock_files) {
-      ContactFrame? frame = frame_for_filename (file_name);
-      if (frame != null)
-        this.stock_thumbnail_grid.add (frame);
-    }
-    this.stock_thumbnail_grid.show_all ();
   }
 
   public void update_preview (FileChooser chooser) {
@@ -240,8 +205,9 @@ public class Contacts.AvatarDialog : Dialog {
         if (file_info != null) {
           var mime_type = file_info.get_content_type ();
 
-          if (mime_type != null)
-            pixbuf = thumbnail_factory.generate_thumbnail (uri, mime_type);
+              //XXX FIXME do this without gnome-desktop pls
+          /* if (mime_type != null) */
+          /*   pixbuf = thumbnail_factory.generate_thumbnail (uri, mime_type); */
         }
       } catch (GLib.Error e) {
       }
@@ -271,30 +237,30 @@ public class Contacts.AvatarDialog : Dialog {
     this.views_stack.set_visible_child_name ("crop-page");
   }
 
-  public override void response (int response_id) {
-    if (response_id == ResponseType.OK && this.new_pixbuf != null) {
-      try {
-        uint8[] buffer;
-        if (this.new_pixbuf.save_to_buffer (out buffer, "png", null)) {
-          var icon = new BytesIcon (new Bytes (buffer));
-          set_avatar (icon);
-        } else {
-          /* Failure. Fall through. */
-        }
-      } catch {
-      }
-    }
-
-#if HAVE_CHEESE
-    /* Ensure the Vala garbage collector disposes of the Cheese widget.
-     * This prevents the 'Device or resource busy' warnings, see:
-     *   https://bugzilla.gnome.org/show_bug.cgi?id=700959
-     */
-    this.cheese = null;
-#endif
-
-    this.destroy ();
-  }
+  /* public override void response (int response_id) {*/
+  /*   if (response_id == ResponseType.OK && this.new_pixbuf != null) {*/
+  /*     try {*/
+  /*       uint8[] buffer;*/
+  /*       if (this.new_pixbuf.save_to_buffer (out buffer, "png", null)) {*/
+  /*         var icon = new BytesIcon (new Bytes (buffer));*/
+  /*         set_avatar (icon);*/
+  /*       } else {*/
+           /* Failure. Fall through. */
+  /*       }*/
+  /*     } catch {*/
+  /*     }*/
+  /*   }*/
+
+/* #if HAVE_CHEESE*/
+     /* Ensure the Vala garbage collector disposes of the Cheese widget.
+      * This prevents the 'Device or resource busy' warnings, see:
+      *   https://bugzilla.gnome.org/show_bug.cgi?id=700959
+      */
+  /*   this.cheese = null;*/
+/* #endif*/
+
+  /*   this.destroy ();*/
+  /* }*/
 
   [GtkCallback]
   private void select_avatar_file_cb (Button button) {
@@ -332,7 +298,7 @@ public class Contacts.AvatarDialog : Dialog {
           else
             selected_pixbuf (scale_pixbuf_for_avatar_use (pixbuf));
 
-          update_thumbnail_grids ();
+          update_thumbnail_grid ();
         } catch {
         }
 
diff --git a/src/contacts-contact-editor.vala b/src/contacts-contact-editor.vala
index b94dba5..72626c7 100644
--- a/src/contacts-contact-editor.vala
+++ b/src/contacts-contact-editor.vala
@@ -978,23 +978,23 @@ public class Contacts.ContactEditor : Grid {
     this.container_grid.attach (this.avatar_frame, 0, 0, 1, 3);
   }
 
-  // Show the avatar dialog when the avatar is clicked
+  // Show the avatar popover when the avatar is clicked
   private void on_avatar_frame_clicked () {
-    var dialog = new AvatarDialog ((Window) get_toplevel (), this.contact);
-    dialog.set_avatar.connect ( (icon) =>  {
-        this.avatar_frame.set_data ("value", icon);
-        this.avatar_frame.set_data ("changed", true);
-
-        Gdk.Pixbuf? a_pixbuf = null;
-        try {
-          var stream = (icon as LoadableIcon).load (PROFILE_SIZE, null);
-          a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, PROFILE_SIZE, PROFILE_SIZE, true);
-        } catch {
-        }
-
-        this.avatar_frame.set_pixbuf (a_pixbuf);
-      });
-    dialog.run ();
+    var popover = new AvatarPopover (this.avatar_frame, this.contact);
+    /* dialog.set_avatar.connect ( (icon) =>  { */
+    /*     this.avatar_frame.set_data ("value", icon); */
+    /*     this.avatar_frame.set_data ("changed", true); */
+
+    /*     Gdk.Pixbuf? a_pixbuf = null; */
+    /*     try { */
+    /*       var stream = (icon as LoadableIcon).load (PROFILE_SIZE, null); */
+    /*       a_pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, PROFILE_SIZE, PROFILE_SIZE, true); */
+    /*     } catch { */
+    /*     } */
+
+    /*     this.avatar_frame.set_pixbuf (a_pixbuf); */
+    /*   }); */
+    popover.show ();
   }
 
   public bool avatar_changed () {
diff --git a/src/contacts-contact-frame.vala b/src/contacts-contact-frame.vala
index b1ec072..c216d63 100644
--- a/src/contacts-contact-frame.vala
+++ b/src/contacts-contact-frame.vala
@@ -23,15 +23,17 @@ using Gee;
 public class Contacts.ContactFrame : Frame {
   private int size;
   private Gdk.Pixbuf? pixbuf;
-  private Pango.Layout? layout;
+  private Contact? contact;
 
   public signal void clicked ();
 
   public ContactFrame (int size, bool with_button = false) {
     this.size = size;
+    this.contact = null;
 
-    var image = new Image ();
+    var image = new DrawingArea ();
     image.set_size_request (size, size);
+    //TODO Border (or not? what if we have a color)
 
     if (with_button) {
       var button = new Button ();
@@ -51,61 +53,115 @@ public class Contacts.ContactFrame : Frame {
     }
 
     image.show ();
-    image.draw.connect (draw_image);
+    image.draw.connect (on_draw_avatar);
 
     set_shadow_type (ShadowType.NONE);
   }
 
-  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 != null)? Contact.frame_icon (a_pixbuf) : null;
     queue_draw ();
   }
 
   public void set_image (AvatarDetails? details, Contact? contact = null) {
+    this.contact = contact;
+
     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);
+        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);
   }
 
-  public bool draw_image (Cairo.Context cr) {
+  public bool on_draw_avatar (Cairo.Context cr) {
     cr.save ();
 
-    if (pixbuf != null) {
-      Gdk.cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
-      cr.paint();
-    }
+    if (this.pixbuf != null)
+      draw_pixbuf (cr);
+    else // Draw the standard person fallback
+      draw_initial (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_pixbuf (Cairo.Context cr) {
+    Gdk.cairo_set_source_pixbuf (cr, this.pixbuf, 0, 0);
+    // Clip with a circle
+    cr.arc (this.size / 2, this.size / 2, (this.size - 1) / 2, 0, 2*Math.PI);
+    cr.clip_preserve ();
+    cr.paint ();
+
+    // Draw a border
+    cr.arc (this.size / 2, this.size / 2, (this.size - 1) / 2, 0, 2*Math.PI);
+    cr.set_line_width (0.5);
+    cr.set_source_rgb (0, 0, 0);
+    cr.stroke ();
+  }
+
+  private void draw_initial (Cairo.Context cr) {
+    // The background colors
+    double bg_r, bg_g, bg_b;
+    get_background_color (out bg_r, out bg_g, out bg_b);
+    // The foreground colors
+    var fg_r = bg_r * 0.5;
+    var fg_g = bg_g * 0.5;
+    var fg_b = bg_b * 0.5;
+
+    // Draw the background circle
+    cr.set_source_rgb (bg_r, bg_g, bg_b);
+    cr.arc (this.size / 2, this.size / 2, (this.size - 1) / 2, 0, 2*Math.PI);
+    cr.fill_preserve ();
+    // Draw the border
+    cr.set_line_width (0.5);
+    cr.set_source_rgb (fg_r, fg_g, fg_b);
+    cr.stroke ();
+
+    // Draw the initial
+    if (this.contact != null && this.contact.display_name != "") {
+      var initial = this.contact.display_name.get_char_validated ();
+      if (initial == -1)
+        return;
+      var initial_upper = initial.totitle ().to_string ();
+
+      // Get the styling right
+      cr.set_source_rgb (fg_r, fg_g, fg_b);
+      //XXX no better way to do this (ie without hardcoding Cantarell)
+      cr.select_font_face ("Cantarell", Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL);
+      cr.set_font_size (this.size * 0.66);
+      // Center it
+      Cairo.TextExtents extents;
+      cr.text_extents (initial_upper, out extents);
+      cr.move_to ((this.size - extents.width) / 2 - extents.x_bearing,
+                  (this.size - extents.height) / 2 - extents.y_bearing);
+
+      cr.show_text (initial_upper);
+    }
+  }
+
+  private void get_background_color (out double r, out double g, out double b) {
+    //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);
+
+    r = ((hash & 0xFF0000) >> 16) / 255.0;
+    g = ((hash & 0x00FF00) >> 8) / 255.0;
+    b = (hash & 0x0000FF) / 255.0;
+
+    // Make it a bit lighter by default (and since the foreground will be darker)
+    r = (r + 2) / 3.0;
+    g = (g + 2) / 3.0;
+    b = (b + 2) / 3.0;
+  }
 }
diff --git a/src/contacts-utils.vala b/src/contacts-utils.vala
index 954e1f2..b828467 100644
--- a/src/contacts-utils.vala
+++ b/src/contacts-utils.vala
@@ -273,28 +273,6 @@ namespace Contacts.Utils {
     entry.select_region (start, end);
   }
 
-  public string[] get_stock_avatars () {
-    string[] files = {};
-    var system_data_dirs = Environment.get_system_data_dirs ();
-    foreach (var data_dir in system_data_dirs) {
-      var path = Path.build_filename (data_dir, "pixmaps", "faces");
-      Dir? dir = null;
-      try {
-        dir = Dir.open (path);
-      } catch (Error e) {
-        debug ("Couldn't open stock avatars folder \"%s\": %s", path, e.message);
-      }
-      if (dir != null) {
-        string? face;
-        while ((face = dir.read_name ()) != null) {
-          var filename = Path.build_filename (path, face);
-          files += filename;
-        }
-      }
-    };
-    return files;
-  }
-
   public PersonaStore[] get_eds_address_books (Store contacts_store) {
     PersonaStore[] stores = {};
     foreach (var backend in contacts_store.backend_store.enabled_backends.values) {
diff --git a/src/meson.build b/src/meson.build
index 2a387c3..f8067be 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -42,7 +42,6 @@ contacts_c_sources = [
 
 contacts_c_args = [
   '-include', 'config.h',
-  '-DGNOME_DESKTOP_USE_UNSTABLE_API',
   '-DLOCALEDIR="@0@"'.format(locale_dir),
 ]
 
@@ -52,7 +51,6 @@ contacts_deps = [
   gee,
   gio_unix,
   glib,
-  gnome_desktop,
   goa,
   gtk,
   libebook,



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