summaryrefslogtreecommitdiff
path: root/src/contacts-avatar-dialog.vala
diff options
context:
space:
mode:
authorNiels De Graef <nielsdegraef@gmail.com>2017-06-26 11:35:20 +0200
committerNiels De Graef <nielsdegraef@gmail.com>2017-06-26 12:20:05 +0200
commita59a7bcc371ec9ebaf74cd8ba488298a209a785e (patch)
treef958cda60591cee3c91fa29d92ee63beabaf09f0 /src/contacts-avatar-dialog.vala
parent62fd4acf920b651da8d31825476d1b88b890af79 (diff)
downloadgnome-contacts-a59a7bcc371ec9ebaf74cd8ba488298a209a785e.tar.gz
Implement AvatarDialog as a GtkTemplate.
* The thumbnails are now loaded using a Gtk.FlowBox, which saves a lot of code. * Cheese's `coldplug()` is now done asynchronously, since it otherwise blocks the UI for several seconds. * Minor formatting changes. * Add some comments to make things clearer.
Diffstat (limited to 'src/contacts-avatar-dialog.vala')
-rw-r--r--src/contacts-avatar-dialog.vala547
1 files changed, 227 insertions, 320 deletions
diff --git a/src/contacts-avatar-dialog.vala b/src/contacts-avatar-dialog.vala
index b9674e0..bef1156 100644
--- a/src/contacts-avatar-dialog.vala
+++ b/src/contacts-avatar-dialog.vala
@@ -19,16 +19,42 @@
using Gtk;
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.
+ *
+ * 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 {
- private Gnome.DesktopThumbnailFactory thumbnail_factory;
- const int main_size = 128;
- const int icons_size = 64;
- const int n_columns = 6;
+ 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 thumbnail_grid;
+ [GtkChild]
+ private Grid crop_page;
private Um.CropArea crop_area;
- private Grid view_grid;
- private ContactFrame main_frame;
+ [GtkChild]
+ private Grid photobooth_page;
+ [GtkChild]
+ private Button webcam_button;
+ [GtkChild]
+ private Box webcam_button_box;
+
+ private ContactFrame current_avatar;
#if HAVE_CHEESE
private Cheese.Flash flash;
@@ -39,9 +65,86 @@ public class Contacts.AvatarDialog : Dialog {
private Gdk.Pixbuf? new_pixbuf;
+ /**
+ * Fired after the user has definitely chosen a new avatar.
+ */
public signal void set_avatar (GLib.Icon avatar_icon);
- Gdk.Pixbuf scale_pixbuf_for_avatar_use (Gdk.Pixbuf pixbuf) {
+ public AvatarDialog (Contact? contact) {
+ Object (
+ transient_for: App.app.window,
+ use_header_bar: 1
+ );
+
+ this.thumbnail_factory = new Gnome.DesktopThumbnailFactory (Gnome.ThumbnailSize.NORMAL);
+ this.contact = contact;
+
+ // Load the current avatar
+ this.current_avatar = new ContactFrame (MAIN_SIZE);
+ if (contact != null) {
+ contact.keep_widget_uptodate (this.current_avatar, (w) => {
+ (w as ContactFrame).set_image (contact.individual, contact);
+ });
+ } else {
+ this.current_avatar.set_image (null, null);
+ }
+ this.current_avatar.set_hexpand (false);
+ this.current_avatar.show ();
+ 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 ( () => {
+ this.num_cameras++;
+ this.webcam_button.sensitive = (this.num_cameras > 0);
+ });
+ this.camera_monitor.removed.connect ( () => {
+ this.num_cameras--;
+ this.webcam_button.sensitive = (this.num_cameras > 0);
+ });
+ // Do this in idle, or it blocks the whole UI
+ Idle.add ( () => {
+ this.camera_monitor.coldplug ();
+ return false;
+ });
+#else
+ // Don't show the camera button
+ this.webcam_button_box.hide ();
+#endif
+
+#if HAVE_CHEESE
+ // Photobooth page
+ this.cheese = new Cheese.Widget ();
+ this.cheese.set_vexpand (true);
+ this.cheese.set_hexpand (true);
+ this.cheese.set_no_show_all (true);
+ this.photobooth_page.attach (cheese, 0, 0);
+ this.photobooth_page.show ();
+
+ this.flash = new Cheese.Flash (this);
+#endif
+
+ this.views_stack.set_visible_child_name ("thumbnail-page");
+ /*
+ var remove_button = new ToolButton (null, null);
+ remove_button.set_icon_name ("list-remove-symbolic");
+ remove_button.get_style_context ().add_class (STYLE_CLASS_RAISED);
+ remove_button.is_important = true;
+ toolbar.add (remove_button);
+ remove_button.clicked.connect ( (button) => {
+ });
+ */
+
+ update_thumbnail_grid ();
+ }
+
+ private Gdk.Pixbuf scale_pixbuf_for_avatar_use (Gdk.Pixbuf pixbuf) {
int w = pixbuf.get_width ();
int h = pixbuf.get_height ();
@@ -60,12 +163,12 @@ public class Contacts.AvatarDialog : Dialog {
}
private ContactFrame create_frame (Gdk.Pixbuf source_pixbuf) {
- var image_frame = new ContactFrame (icons_size, true);
- var pixbuf = source_pixbuf.scale_simple (icons_size, icons_size, Gdk.InterpType.HYPER);
+ var image_frame = new ContactFrame (ICONS_SIZE, true);
+ var pixbuf = source_pixbuf.scale_simple (ICONS_SIZE, ICONS_SIZE, Gdk.InterpType.HYPER);
image_frame.set_pixbuf (pixbuf);
var avatar_pixbuf = scale_pixbuf_for_avatar_use (source_pixbuf);
image_frame.clicked.connect ( () => {
- selected_pixbuf (avatar_pixbuf);
+ selected_pixbuf (avatar_pixbuf);
});
return image_frame;
}
@@ -97,55 +200,30 @@ public class Contacts.AvatarDialog : Dialog {
}
private void selected_pixbuf (Gdk.Pixbuf pixbuf) {
- var p = pixbuf.scale_simple (main_size, main_size, Gdk.InterpType.HYPER);
- main_frame.set_pixbuf (p);
+ var p = pixbuf.scale_simple (MAIN_SIZE, MAIN_SIZE, Gdk.InterpType.HYPER);
+ this.current_avatar.set_pixbuf (p);
- new_pixbuf = pixbuf;
+ this.new_pixbuf = pixbuf;
set_response_sensitive (ResponseType.OK, true);
}
- private void update_grid () {
- int i = 0;
- int j = 0;
-
- if (contact != null) {
+ private void update_thumbnail_grid () {
+ if (this.contact != null) {
foreach (var p in contact.individual.personas) {
- ContactFrame? frame = frame_for_persona (p);
- if (frame != null) {
- view_grid.attach (frame, i, j, 1, 1);
- i++;
- if (i >= n_columns) {
- i -= n_columns;
- j++;
- }
- }
+ ContactFrame? frame = frame_for_persona (p);
+ if (frame != null)
+ this.thumbnail_grid.add (frame);
}
}
- if (i != 0) {
- i = 0;
- j++;
- }
-
- if (j != 0) {
- var s = new Separator (Orientation.HORIZONTAL);
- view_grid.attach (s, 0, j++, n_columns, 1);
- }
-
var stock_files = Utils.get_stock_avatars ();
foreach (var file_name in stock_files) {
ContactFrame? frame = frame_for_filename (file_name);
- if (frame != null) {
- view_grid.attach (frame, i, j, 1, 1);
- i++;
- if (i >= n_columns) {
- i -= n_columns;
- j++;
- }
- }
+ if (frame != null)
+ this.thumbnail_grid.add (frame);
}
- view_grid.show_all ();
+ this.thumbnail_grid.show_all ();
}
public void update_preview (FileChooser chooser) {
@@ -157,51 +235,74 @@ public class Contacts.AvatarDialog : Dialog {
var file = File.new_for_uri (uri);
try {
- var file_info = file.query_info (FileAttribute.STANDARD_CONTENT_TYPE,
- FileQueryInfoFlags.NONE, null);
- if (file_info != null) {
- var mime_type = file_info.get_content_type ();
-
- if (mime_type != null)
- pixbuf = thumbnail_factory.generate_thumbnail (uri, mime_type);
- }
+ var file_info = file.query_info (FileAttribute.STANDARD_CONTENT_TYPE,
+ FileQueryInfoFlags.NONE, null);
+ if (file_info != null) {
+ var mime_type = file_info.get_content_type ();
+
+ if (mime_type != null)
+ pixbuf = thumbnail_factory.generate_thumbnail (uri, mime_type);
+ }
} catch (GLib.Error e) {
}
- (chooser as Dialog).set_response_sensitive (ResponseType.ACCEPT,
- (pixbuf != null));
+ (chooser as Dialog).set_response_sensitive (ResponseType.ACCEPT, (pixbuf != null));
if (pixbuf != null)
- preview.set_from_pixbuf (pixbuf);
+ preview.set_from_pixbuf (pixbuf);
else
- preview.set_from_icon_name ("dialog-question",
- IconSize.DIALOG);
+ preview.set_from_icon_name ("dialog-question", IconSize.DIALOG);
}
chooser.set_preview_widget_active (true);
}
private void set_crop_widget (Gdk.Pixbuf pixbuf) {
- var frame_grid = views_stack.get_child_by_name ("crop-page") as Grid;
- crop_area = new Um.CropArea ();
- crop_area.set_vexpand (true);
- crop_area.set_hexpand (true);
- crop_area.set_min_size (48, 48);
- crop_area.set_constrain_aspect (true);
- crop_area.set_picture (pixbuf);
-
- frame_grid.attach (crop_area, 0, 0, 1, 1);
- frame_grid.show_all ();
-
- views_stack.set_visible_child_name ("crop-page");
+ this.crop_area = new Um.CropArea ();
+ this.crop_area.set_vexpand (true);
+ this.crop_area.set_hexpand (true);
+ this.crop_area.set_min_size (48, 48);
+ this.crop_area.set_constrain_aspect (true);
+ this.crop_area.set_picture (pixbuf);
+
+ this.crop_page.attach (this.crop_area, 0, 0);
+ this.crop_page.show_all ();
+
+ this.views_stack.set_visible_child_name ("crop-page");
}
- private void select_avatar_file_cb () {
+ 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) {
var chooser = new FileChooserDialog (_("Browse for more pictures"),
- (Gtk.Window)this.get_toplevel (),
- FileChooserAction.OPEN,
- _("_Cancel"), ResponseType.CANCEL,
- _("_Open"), ResponseType.ACCEPT);
+ (Gtk.Window)this.get_toplevel (),
+ FileChooserAction.OPEN,
+ _("_Cancel"), ResponseType.CANCEL,
+ _("_Open"), ResponseType.ACCEPT);
chooser.set_modal (true);
chooser.set_local_only (false);
var preview = new Image ();
@@ -217,266 +318,72 @@ public class Contacts.AvatarDialog : Dialog {
chooser.set_current_folder (folder);
chooser.response.connect ( (response) => {
- if (response != ResponseType.ACCEPT) {
- chooser.destroy ();
- return;
- }
- try {
- var file = File.new_for_uri (chooser.get_uri ());
- var in_stream = file.read ();
- var pixbuf = new Gdk.Pixbuf.from_stream (in_stream, null);
- in_stream.close ();
- if (pixbuf.get_width () > 128 || pixbuf.get_height () > 128) {
- set_crop_widget (pixbuf);
- } else
- selected_pixbuf (scale_pixbuf_for_avatar_use (pixbuf));
-
- update_grid ();
- } catch {
- }
-
- chooser.destroy ();
+ if (response != ResponseType.ACCEPT) {
+ chooser.destroy ();
+ return;
+ }
+ try {
+ var file = File.new_for_uri (chooser.get_uri ());
+ var in_stream = file.read ();
+ var pixbuf = new Gdk.Pixbuf.from_stream (in_stream, null);
+ in_stream.close ();
+ if (pixbuf.get_width () > 128 || pixbuf.get_height () > 128)
+ set_crop_widget (pixbuf);
+ else
+ selected_pixbuf (scale_pixbuf_for_avatar_use (pixbuf));
+
+ update_thumbnail_grid ();
+ } catch {
+ }
+
+ chooser.destroy ();
});
chooser.present ();
}
- public AvatarDialog (Contact? contact) {
- Object (use_header_bar: 1);
-
- thumbnail_factory = new Gnome.DesktopThumbnailFactory (Gnome.ThumbnailSize.NORMAL);
- this.contact = contact;
- set_title (_("Select Picture"));
- set_transient_for (App.app.window);
- set_modal (true);
-
- var btn = add_button (_("Select"), ResponseType.OK);
- btn.get_style_context ().add_class ("suggested-action");
- add_button (_("Cancel"), ResponseType.CANCEL);
-
- set_default_response (ResponseType.OK);
- set_response_sensitive (ResponseType.OK, false);
-
- var grid = new Grid ();
- grid.set_border_width (8);
- grid.set_column_spacing (16);
- var container = (get_content_area () as Container);
- container.add (grid);
-
- main_frame = new ContactFrame (main_size);
- if (contact != null) {
- contact.keep_widget_uptodate (main_frame, (w) => {
- (w as ContactFrame).set_image (contact.individual, contact);
- });
- } else {
- main_frame.set_image (null, null);
- }
- main_frame.set_hexpand (false);
- grid.attach (main_frame, 0, 0, 1, 1);
-
- var label = new Label ("");
- if (contact != null) {
- label.set_markup (Markup.printf_escaped ("<span font='16'>%s</span>", contact.display_name));
- } else {
- label.set_markup (Markup.printf_escaped ("<span font='16'>%s</span>", _("New Contact")));
- }
- label.set_valign (Align.START);
- label.set_halign (Align.START);
- label.set_hexpand (true);
- label.set_margin_top (4);
- label.xalign = 0.0f;
- label.set_ellipsize (Pango.EllipsizeMode.END);
- grid.attach (label, 1, 0, 1, 1);
-
- grid.set_row_spacing (11);
-
- var frame = new Frame (null);
- frame.get_style_context ().add_class ("contacts-avatar-frame");
- grid.attach (frame, 0, 1, 2, 1);
-
- views_stack = new Stack ();
-
- frame.add (views_stack);
-
- var frame_grid = new Grid ();
- frame_grid.set_orientation (Orientation.VERTICAL);
-
- /* main view */
- var scrolled = new ScrolledWindow(null, null);
- scrolled.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
- scrolled.set_vexpand (true);
- scrolled.set_hexpand (true);
- scrolled.set_size_request (-1, 300);
-
- frame_grid.add (scrolled);
-
- view_grid = new Grid ();
- scrolled.add (view_grid);
-
- var actionbar = new ActionBar ();
- frame_grid.add (actionbar);
-
- var the_add_button = new Button.from_icon_name ("list-add-symbolic",
- IconSize.MENU);
- the_add_button.clicked.connect (select_avatar_file_cb);
-
+ [GtkCallback]
+ private void on_webcam_button_clicked (Button button) {
#if HAVE_CHEESE
- var webcam_button = new Button.from_icon_name ("camera-photo-symbolic",
- IconSize.MENU);
- webcam_button.sensitive = false;
-
- camera_monitor = new Cheese.CameraDeviceMonitor ();
- camera_monitor.added.connect ( () => {
- num_cameras++;
- webcam_button.sensitive = num_cameras > 0;
- });
- camera_monitor.removed.connect ( () => {
- num_cameras--;
- webcam_button.sensitive = num_cameras > 0;
- });
- camera_monitor.coldplug ();
-
- webcam_button.clicked.connect ( (button) => {
- views_stack.set_visible_child_name ("photobooth-page");
- cheese.show ();
- });
-
- var bbox = new Box (Orientation.HORIZONTAL, 0);
- bbox.get_style_context ().add_class ("linked");
- bbox.add (the_add_button);
- bbox.add (webcam_button);
- actionbar.pack_start (bbox);
-
-#else
- actionbar.pack_start (the_add_button);
+ this.views_stack.set_visible_child_name ("photobooth-page");
+ this.cheese.show ();
#endif
+ }
- frame_grid.show_all ();
- views_stack.add_named (frame_grid, "thumbnail-factory");
-
- /* crop page */
- frame_grid = new Grid ();
- frame_grid.set_orientation (Orientation.VERTICAL);
-
- actionbar = new ActionBar ();
- frame_grid.attach (actionbar, 0, 1, 1, 1);
-
- var accept_button = new Button.from_icon_name ("object-select-symbolic",
- IconSize.MENU);
- accept_button.clicked.connect ( (button) => {
- var pix = crop_area.get_picture ();
- selected_pixbuf (scale_pixbuf_for_avatar_use (pix));
- crop_area.destroy ();
- views_stack.set_visible_child_name ("thumbnail-factory");
- });
-
- var cancel_button = new Button.from_icon_name ("edit-undo-symbolic",
- IconSize.MENU);
- cancel_button.clicked.connect ( (button) => {
- crop_area.destroy ();
- views_stack.set_visible_child_name ("thumbnail-factory");
- });
-
- var bbox1 = new Box (Orientation.HORIZONTAL, 0);
- bbox1.get_style_context ().add_class ("linked");
- bbox1.add (accept_button);
- bbox1.add (cancel_button);
- actionbar.pack_start (bbox1);
-
- frame_grid.show_all ();
- views_stack.add_named (frame_grid, "crop-page");
-
+ [GtkCallback]
+ private void on_photobooth_page_select_button_clicked (Button button) {
#if HAVE_CHEESE
- /* photobooth page */
- frame_grid = new Grid ();
- frame_grid.set_orientation (Orientation.VERTICAL);
-
- cheese = new Cheese.Widget ();
- cheese.set_vexpand (true);
- cheese.set_hexpand (true);
- cheese.set_no_show_all (true);
- frame_grid.add (cheese);
-
- flash = new Cheese.Flash (this);
-
- actionbar = new ActionBar ();
- frame_grid.attach (actionbar, 0, 1, 1, 1);
-
- accept_button = new Button.from_icon_name ("object-select-symbolic",
- IconSize.MENU);
-
- accept_button.clicked.connect ( (button) => {
- var camera = cheese.get_camera () as Cheese.Camera;
-
- flash.fire ();
-
- camera.photo_taken.connect ( (pix) => {
- set_crop_widget (pix);
- cheese.hide ();
- });
-
- if (!camera.take_photo_pixbuf ()) {
- warning ("Unable to take photo");
- }
+ var camera = this.cheese.get_camera () as Cheese.Camera;
+ this.flash.fire ();
+ camera.photo_taken.connect ( (pix) => {
+ set_crop_widget (pix);
+ this.cheese.hide ();
});
- cancel_button = new Button.from_icon_name ("edit-undo-symbolic",
- IconSize.MENU);
- cancel_button.clicked.connect ( (button) => {
- views_stack.set_visible_child_name ("thumbnail-factory");
- cheese.hide ();
- });
-
- bbox1 = new Box (Orientation.HORIZONTAL, 0);
- bbox1.get_style_context ().add_class ("linked");
- bbox1.add (accept_button);
- bbox1.add (cancel_button);
- actionbar.pack_start (bbox1);
-
- frame_grid.show_all ();
- views_stack.add_named (frame_grid, "photobooth-page");
+ if (!camera.take_photo_pixbuf ())
+ warning ("Unable to take photo");
#endif
+ }
- views_stack.set_visible_child_name ("thumbnail-factory");
- /*
- var remove_button = new ToolButton (null, null);
- remove_button.set_icon_name ("list-remove-symbolic");
- remove_button.get_style_context ().add_class (STYLE_CLASS_RAISED);
- remove_button.is_important = true;
- toolbar.add (remove_button);
- remove_button.clicked.connect ( (button) => {
- });
- */
-
- response.connect ( (response_id) => {
- if (response_id == ResponseType.OK) {
- if (new_pixbuf != null) {
- try {
- uint8[] buffer;
- if (new_pixbuf.save_to_buffer (out buffer, "png", null)) {
- var icon = new BytesIcon (new Bytes (buffer));
- set_avatar (icon);
- } else {
- /* Failure. Fall through. */
- }
- } catch {
- }
- }
- }
-
+ [GtkCallback]
+ private void on_photobooth_page_cancel_button_clicked (Button button) {
#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
- */
- cheese = null;
+ this.views_stack.set_visible_child_name ("thumbnail-page");
+ this.cheese.hide ();
#endif
+ }
- this.destroy ();
- });
-
- update_grid ();
+ [GtkCallback]
+ private void on_crop_page_select_button_clicked (Button button) {
+ var pix = crop_area.get_picture ();
+ selected_pixbuf (scale_pixbuf_for_avatar_use (pix));
+ this.crop_area.destroy ();
+ this.views_stack.set_visible_child_name ("thumbnail-page");
+ }
- grid.show_all ();
+ [GtkCallback]
+ private void on_crop_page_cancel_button_clicked (Button button) {
+ this.crop_area.destroy ();
+ this.views_stack.set_visible_child_name ("thumbnail-page");
}
}