Skip to content
Snippets Groups Projects
Commit ae92efd6 authored by Stepan Salenikovich's avatar Stepan Salenikovich Committed by Alexandre Lision
Browse files

improve avatar manipulation

Changes interface to mimick the one in Gnome Contacts:
- no more trash or export buttons
- the first choice is to take a photo or import
- adds return/undo buttons during selection process

Change-Id: I432f4bad48c1379f6fb26569edea32a24cbb98e9
Tuleap: #840
parent 02b283d7
No related branches found
No related tags found
No related merge requests found
...@@ -72,11 +72,18 @@ struct _AvatarManipulationPrivate ...@@ -72,11 +72,18 @@ struct _AvatarManipulationPrivate
GtkWidget *video_widget; GtkWidget *video_widget;
GtkWidget *box_views_and_controls; GtkWidget *box_views_and_controls;
GtkWidget *box_controls; GtkWidget *box_controls;
GtkWidget *button_take_photo;
GtkWidget *button_box_current;
GtkWidget *button_box_photo;
GtkWidget *button_box_edit;
GtkWidget *button_start_camera;
GtkWidget *button_choose_picture; GtkWidget *button_choose_picture;
GtkWidget *button_trash_avatar; GtkWidget *button_take_photo;
GtkWidget *button_return_photo;
GtkWidget *button_set_avatar; GtkWidget *button_set_avatar;
GtkWidget *button_export_avatar; GtkWidget *button_return_edit;
GtkWidget *selector_widget; GtkWidget *selector_widget;
GtkWidget *stack_views; GtkWidget *stack_views;
GtkWidget *image_avatar; GtkWidget *image_avatar;
...@@ -93,6 +100,13 @@ struct _AvatarManipulationPrivate ...@@ -93,6 +100,13 @@ struct _AvatarManipulationPrivate
GdkModifierType button_pressed; GdkModifierType button_pressed;
AvatarManipulationState state; AvatarManipulationState state;
AvatarManipulationState last_state;
/* this is used to keep track of the state of the preview when the camera is used to take a
* photo; if a call is in progress, then the preview should already be started and we don't want
* to stop it when the settings are closed, in this case
*/
gboolean video_started_by_avatar_manipulation;
}; };
G_DEFINE_TYPE_WITH_PRIVATE(AvatarManipulation, avatar_manipulation, GTK_TYPE_BOX); G_DEFINE_TYPE_WITH_PRIVATE(AvatarManipulation, avatar_manipulation, GTK_TYPE_BOX);
...@@ -102,11 +116,11 @@ G_DEFINE_TYPE_WITH_PRIVATE(AvatarManipulation, avatar_manipulation, GTK_TYPE_BOX ...@@ -102,11 +116,11 @@ G_DEFINE_TYPE_WITH_PRIVATE(AvatarManipulation, avatar_manipulation, GTK_TYPE_BOX
static void set_state(AvatarManipulation *self, AvatarManipulationState state); static void set_state(AvatarManipulation *self, AvatarManipulationState state);
static void start_camera(AvatarManipulation *self);
static void take_a_photo(AvatarManipulation *self); static void take_a_photo(AvatarManipulation *self);
static void choose_picture(AvatarManipulation *self); static void choose_picture(AvatarManipulation *self);
static void export_avatar(AvatarManipulation *self); static void return_to_previous_state(AvatarManipulation *self);
static void update_preview_cb(GtkFileChooser *file_chooser, GtkWidget *preview); static void update_preview_cb(GtkFileChooser *file_chooser, GtkWidget *preview);
static void trash_photo(AvatarManipulation *self);
static void set_avatar(AvatarManipulation *self); static void set_avatar(AvatarManipulation *self);
static void got_snapshot(AvatarManipulation *parent); static void got_snapshot(AvatarManipulation *parent);
...@@ -125,6 +139,7 @@ avatar_manipulation_dispose(GObject *object) ...@@ -125,6 +139,7 @@ avatar_manipulation_dispose(GObject *object)
AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(object); AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(object);
/* make sure we stop the preview and the video widget */ /* make sure we stop the preview and the video widget */
if (priv->video_started_by_avatar_manipulation)
Video::PreviewManager::instance().stopPreview(); Video::PreviewManager::instance().stopPreview();
if (priv->video_widget) { if (priv->video_widget) {
gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget); gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget);
...@@ -162,9 +177,12 @@ avatar_manipulation_new_from_wizard(void) ...@@ -162,9 +177,12 @@ avatar_manipulation_new_from_wizard(void)
{ {
auto self = avatar_manipulation_new(); auto self = avatar_manipulation_new();
/* in this mode, we want to automatically go to the new avatar state, unless one already exists */ /* in this mode, we want to automatically go to the PHOTO avatar state, unless one already exists */
if (!ProfileModel::instance().selectedProfile()->person()->photo().isValid()) if (!ProfileModel::instance().selectedProfile()->person()->photo().isValid()) {
set_state(AVATAR_MANIPULATION(self), AVATAR_MANIPULATION_STATE_NEW); // check if there is a camera
if (Video::DeviceModel::instance().rowCount() > 0)
set_state(AVATAR_MANIPULATION(self), AVATAR_MANIPULATION_STATE_PHOTO);
}
return self; return self;
} }
...@@ -179,14 +197,18 @@ avatar_manipulation_class_init(AvatarManipulationClass *klass) ...@@ -179,14 +197,18 @@ avatar_manipulation_class_init(AvatarManipulationClass *klass)
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, box_views_and_controls); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, box_views_and_controls);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, box_controls); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, box_controls);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_start_camera);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_choose_picture);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_take_photo); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_take_photo);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_return_photo);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_set_avatar); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_set_avatar);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_trash_avatar); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_return_edit);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_choose_picture);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_export_avatar);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, stack_views); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, stack_views);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, image_avatar); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, image_avatar);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, vbox_selector); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, vbox_selector);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_current);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_photo);
gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_edit);
} }
static void static void
...@@ -216,11 +238,12 @@ avatar_manipulation_init(AvatarManipulation *self) ...@@ -216,11 +238,12 @@ avatar_manipulation_init(AvatarManipulation *self)
/* signals */ /* signals */
g_signal_connect_swapped(priv->button_start_camera, "clicked", G_CALLBACK(start_camera), self);
g_signal_connect_swapped(priv->button_choose_picture, "clicked", G_CALLBACK(choose_picture), self); g_signal_connect_swapped(priv->button_choose_picture, "clicked", G_CALLBACK(choose_picture), self);
g_signal_connect_swapped(priv->button_take_photo, "clicked", G_CALLBACK(take_a_photo), self); g_signal_connect_swapped(priv->button_take_photo, "clicked", G_CALLBACK(take_a_photo), self);
g_signal_connect_swapped(priv->button_return_photo, "clicked", G_CALLBACK(return_to_previous_state), self);
g_signal_connect_swapped(priv->button_set_avatar, "clicked", G_CALLBACK(set_avatar), self); g_signal_connect_swapped(priv->button_set_avatar, "clicked", G_CALLBACK(set_avatar), self);
g_signal_connect_swapped(priv->button_trash_avatar, "clicked", G_CALLBACK(trash_photo), self); g_signal_connect_swapped(priv->button_return_edit, "clicked", G_CALLBACK(return_to_previous_state), self);
g_signal_connect_swapped(priv->button_export_avatar, "clicked", G_CALLBACK(export_avatar), self);
set_state(self, AVATAR_MANIPULATION_STATE_CURRENT); set_state(self, AVATAR_MANIPULATION_STATE_CURRENT);
...@@ -234,6 +257,9 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) ...@@ -234,6 +257,9 @@ set_state(AvatarManipulation *self, AvatarManipulationState state)
// been done by the caller // been done by the caller
AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self); AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
// save prev state
priv->last_state = priv->state;
switch (state) { switch (state) {
case AVATAR_MANIPULATION_STATE_CURRENT: case AVATAR_MANIPULATION_STATE_CURRENT:
{ {
...@@ -251,16 +277,18 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) ...@@ -251,16 +277,18 @@ set_state(AvatarManipulation *self, AvatarManipulationState state)
} }
gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_avatar"); gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_avatar");
// gtk_widget_set_size_request(priv->video_widget, VIDEO_WIDTH, VIDEO_HEIGHT);
/* available actions: trash or export avatar */ /* available actions: start camera (if available) or choose image */
gtk_widget_set_visible(priv->button_trash_avatar, true); if (Video::DeviceModel::instance().rowCount() > 0) {
gtk_widget_set_visible(priv->button_export_avatar, true); // TODO: update if a video device gets inserted while in this state
gtk_widget_set_visible(priv->button_set_avatar, false); gtk_widget_set_visible(priv->button_start_camera, true);
gtk_widget_set_visible(priv->button_take_photo, false); }
gtk_widget_set_visible(priv->button_choose_picture, false); gtk_widget_set_visible(priv->button_box_current, true);
gtk_widget_set_visible(priv->button_box_photo, false);
gtk_widget_set_visible(priv->button_box_edit, false);
/* make sure video widget and camera is no longer running */ /* make sure video widget and camera is not running */
if (priv->video_started_by_avatar_manipulation)
Video::PreviewManager::instance().stopPreview(); Video::PreviewManager::instance().stopPreview();
if (priv->video_widget) { if (priv->video_widget) {
gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget); gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget);
...@@ -268,19 +296,10 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) ...@@ -268,19 +296,10 @@ set_state(AvatarManipulation *self, AvatarManipulationState state)
} }
} }
break; break;
case AVATAR_MANIPULATION_STATE_NEW: case AVATAR_MANIPULATION_STATE_PHOTO:
{ {
/* check if a video device is available, if so show the camera stream; otherwise we can // start the video; if its not available we should not be in this state
* only choose a file, and we keep showing the current/default avatar
*
* TODO: update if a video device gets inserted while in this state
*/
if (Video::DeviceModel::instance().rowCount() > 0) {
priv->video_widget = video_widget_new(); priv->video_widget = video_widget_new();
// gtk_widget_set_size_request(priv->video_widget, VIDEO_WIDTH, VIDEO_HEIGHT);
video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
Video::PreviewManager::instance().previewRenderer(),
VIDEO_RENDERER_REMOTE);
g_signal_connect_swapped(priv->video_widget, "snapshot-taken", G_CALLBACK (got_snapshot), self); g_signal_connect_swapped(priv->video_widget, "snapshot-taken", G_CALLBACK (got_snapshot), self);
gtk_widget_set_vexpand_set(priv->video_widget, FALSE); gtk_widget_set_vexpand_set(priv->video_widget, FALSE);
gtk_widget_set_hexpand_set(priv->video_widget, FALSE); gtk_widget_set_hexpand_set(priv->video_widget, FALSE);
...@@ -288,24 +307,29 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) ...@@ -288,24 +307,29 @@ set_state(AvatarManipulation *self, AvatarManipulationState state)
gtk_stack_add_named(GTK_STACK(priv->stack_views), priv->video_widget, "page_photobooth"); gtk_stack_add_named(GTK_STACK(priv->stack_views), priv->video_widget, "page_photobooth");
gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_photobooth"); gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_photobooth");
Video::PreviewManager::instance().startPreview();
/* available action in this case: take snapshot */ /* local renderer, but set as "remote" so that it takes up the whole screen */
gtk_widget_set_visible(priv->button_take_photo, true); video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
Video::PreviewManager::instance().previewRenderer(),
VIDEO_RENDERER_REMOTE);
if (!Video::PreviewManager::instance().isPreviewing()) {
priv->video_started_by_avatar_manipulation = TRUE;
Video::PreviewManager::instance().startPreview();
} else { } else {
gtk_widget_set_visible(priv->button_take_photo, false); priv->video_started_by_avatar_manipulation = FALSE;
} }
/* available actions: choose file */ /* available actions: take snapshot, return*/
gtk_widget_set_visible(priv->button_choose_picture, true); gtk_widget_set_visible(priv->button_box_current, false);
gtk_widget_set_visible(priv->button_trash_avatar, false); gtk_widget_set_visible(priv->button_box_photo, true);
gtk_widget_set_visible(priv->button_export_avatar, false); gtk_widget_set_visible(priv->button_box_edit, false);
gtk_widget_set_visible(priv->button_set_avatar, false);
} }
break; break;
case AVATAR_MANIPULATION_STATE_EDIT: case AVATAR_MANIPULATION_STATE_EDIT:
{ {
/* make sure video widget and camera is no longer running */ /* make sure video widget and camera is not running */
if (priv->video_started_by_avatar_manipulation)
Video::PreviewManager::instance().stopPreview(); Video::PreviewManager::instance().stopPreview();
if (priv->video_widget) { if (priv->video_widget) {
gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget); gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget);
...@@ -317,12 +341,10 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) ...@@ -317,12 +341,10 @@ set_state(AvatarManipulation *self, AvatarManipulationState state)
priv->origin[1] = 0; priv->origin[1] = 0;
priv->length = INITIAL_LENTGH; priv->length = INITIAL_LENTGH;
/* available actions: set avatar, trash avatar */ /* available actions: set avatar, return */
gtk_widget_set_visible(priv->button_set_avatar, true); gtk_widget_set_visible(priv->button_box_current, false);
gtk_widget_set_visible(priv->button_trash_avatar, true); gtk_widget_set_visible(priv->button_box_photo, false);
gtk_widget_set_visible(priv->button_take_photo, false); gtk_widget_set_visible(priv->button_box_edit, true);
gtk_widget_set_visible(priv->button_choose_picture, false);
gtk_widget_set_visible(priv->button_export_avatar, false);
gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_edit_view"); gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_edit_view");
} }
...@@ -333,20 +355,16 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) ...@@ -333,20 +355,16 @@ set_state(AvatarManipulation *self, AvatarManipulationState state)
} }
static void static void
take_a_photo(AvatarManipulation *self) start_camera(AvatarManipulation *self)
{ {
AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self); set_state(self, AVATAR_MANIPULATION_STATE_PHOTO);
video_widget_take_snapshot(VIDEO_WIDGET(priv->video_widget));
} }
static void static void
trash_photo(AvatarManipulation *self) take_a_photo(AvatarManipulation *self)
{ {
/* clear the avatar in the profile */ AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
ProfileModel::instance().selectedProfile()->person()->setPhoto(QVariant()); video_widget_take_snapshot(VIDEO_WIDGET(priv->video_widget));
ProfileModel::instance().selectedProfile()->save();
set_state(self, AVATAR_MANIPULATION_STATE_NEW);
} }
static void static void
...@@ -389,6 +407,13 @@ set_avatar(AvatarManipulation *self) ...@@ -389,6 +407,13 @@ set_avatar(AvatarManipulation *self)
set_state(self, AVATAR_MANIPULATION_STATE_CURRENT); set_state(self, AVATAR_MANIPULATION_STATE_CURRENT);
} }
static void
return_to_previous_state(AvatarManipulation *self)
{
AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);
set_state(self, priv->last_state);
}
static void static void
choose_picture(AvatarManipulation *self) choose_picture(AvatarManipulation *self)
{ {
...@@ -448,44 +473,6 @@ choose_picture(AvatarManipulation *self) ...@@ -448,44 +473,6 @@ choose_picture(AvatarManipulation *self)
gtk_widget_destroy(dialog); gtk_widget_destroy(dialog);
} }
static void
export_avatar(AvatarManipulation *self)
{
GtkWidget *ring_main_window = gtk_widget_get_toplevel(GTK_WIDGET(self));
auto dialog = gtk_file_chooser_dialog_new(_("Save Avatar Image"),
GTK_WINDOW(ring_main_window),
GTK_FILE_CHOOSER_ACTION_SAVE,
_("_Cancel"),
GTK_RESPONSE_CANCEL,
_("_Save"),
GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
/* start the file chooser */
auto res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_ACCEPT) {
if (auto filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog))) {
GError* error = nullptr;
auto photo = ProfileModel::instance().selectedProfile()->person()->photo();
std::shared_ptr<GdkPixbuf> pixbuf_photo = photo.value<std::shared_ptr<GdkPixbuf>>();
if (photo.isValid()) {
gdk_pixbuf_save(pixbuf_photo.get(), filename, "png", &error, NULL);
if (error){
g_warning("(export_avatar) could not save avatar to file: %s\n", error->message);
g_error_free(error);
}
}
g_free (filename);
}
}
gtk_widget_destroy (dialog);
}
static void static void
update_preview_cb(GtkFileChooser *file_chooser, GtkWidget *preview) update_preview_cb(GtkFileChooser *file_chooser, GtkWidget *preview)
{ {
......
...@@ -37,19 +37,18 @@ typedef struct _AvatarManipulationClass AvatarManipulationClass; ...@@ -37,19 +37,18 @@ typedef struct _AvatarManipulationClass AvatarManipulationClass;
/** /**
* AvatarManipulationState: * AvatarManipulationState:
* @AVATAR_MANIPULATION_STATE_CURRENT: The initial state. The widget will display the current * @AVATAR_MANIPULATION_STATE_CURRENT: The initial state. The widget will display the current
* or the default avatar, with the possible actions being to delete (and select a new one) or to * or the default avatar, with the possible actions being to start the camera (if available) to
* save to disk the current avatar. * take a new photo) or to select an existing image.
* @AVATAR_MANIPULATION_STATE_NEW: The state in which the user is selecting a new avatar. If * @AVATAR_MANIPULATION_STATE_PHOTO: The state in which the camera is on and the video is being
* a video device is available, it will will be turned on. The possible actions being to take a * shown. The possible actions are to take a snapshot or to return to the previous state.
* snapshot or to select an existing image from a file. * @AVATAR_MANIPULATION_STATE_EDIT: The state after selecting a new image (or snapping a photo). The
* @AVATAR_MANIPULATION_STATE_EDIT: The state after selecting a new image when the user must * user must select the square area they wish to use for the avatar. The possible actions are to
* select the square area they wish to use for the avatar. The possible actions being to confirm * confirm the selection or to return to the previous state.
* the selection or to discard it.
*/ */
typedef enum typedef enum
{ {
AVATAR_MANIPULATION_STATE_CURRENT, AVATAR_MANIPULATION_STATE_CURRENT,
AVATAR_MANIPULATION_STATE_NEW, AVATAR_MANIPULATION_STATE_PHOTO,
AVATAR_MANIPULATION_STATE_EDIT AVATAR_MANIPULATION_STATE_EDIT
} AvatarManipulationState; } AvatarManipulationState;
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.19.0 -->
<interface> <interface>
<requires lib="gtk+" version="3.10"/> <requires lib="gtk+" version="3.10"/>
<object class="GtkImage" id="image_add_symbolic"> <object class="GtkImage" id="image_add_symbolic">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-add-symbolic</property> <property name="icon_name">list-add-symbolic</property>
</object> </object>
<object class="GtkImage" id="image_camera_photo"> <object class="GtkImage" id="image_camera_photo">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">camera-photo-symbolic</property> <property name="icon_name">camera-photo-symbolic</property>
</object> </object>
<object class="GtkImage" id="image_save_symbolic"> <object class="GtkImage" id="image_take_photo">
<property name="can_focus">False</property> <property name="visible">True</property>
<property name="icon_name">document-save-symbolic</property> <property name="icon_name">object-select-symbolic</property>
</object> </object>
<object class="GtkImage" id="image_select_symbolic"> <object class="GtkImage" id="image_select_area">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">object-select-symbolic</property> <property name="icon_name">object-select-symbolic</property>
</object> </object>
<object class="GtkImage" id="image_trash_symbolic"> <object class="GtkImage" id="image_return_photo">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="icon_name">edit-undo-symbolic</property>
<property name="icon_name">user-trash-symbolic</property> </object>
<object class="GtkImage" id="image_return_edit">
<property name="visible">True</property>
<property name="icon_name">edit-undo-symbolic</property>
</object> </object>
<template class="AvatarManipulation" parent="GtkBox"> <template class="AvatarManipulation" parent="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
...@@ -73,90 +71,108 @@ ...@@ -73,90 +71,108 @@
<child> <child>
<object class="GtkBox" id="box_controls"> <object class="GtkBox" id="box_controls">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property> <property name="valign">center</property>
<!-- initial state: take photo or choose image -->
<child>
<object class="GtkBox" id="button_box_current">
<style> <style>
<class name="linked"/> <class name="linked"/>
<class name="horizontal"/>
</style> </style>
<child> <child>
<object class="GtkButton" id="button_take_photo"> <object class="GtkButton" id="button_start_camera">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="image">image_camera_photo</property> <property name="image">image_camera_photo</property>
<property name="always_show_image">True</property>
<property name="tooltip-text" translatable="yes">Take photo</property> <property name="tooltip-text" translatable="yes">Take photo</property>
</object> </object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="button_trash_avatar"> <object class="GtkButton" id="button_choose_picture">
<property name="can_focus">True</property> <property name="visible">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="image">image_trash_symbolic</property> <property name="image">image_add_symbolic</property>
<property name="always_show_image">True</property> <property name="tooltip-text" translatable="yes">Choose image from file</property>
<property name="tooltip-text" translatable="yes">Delete current image</property> </object>
</child>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">False</property>
<property name="position">1</property>
</packing> </packing>
</child> </child>
<!-- 2nd state: snap photo or return -->
<child> <child>
<object class="GtkButton" id="button_set_avatar"> <object class="GtkBox" id="button_box_photo">
<property name="can_focus">True</property> <style>
<class name="linked"/>
</style>
<child>
<object class="GtkButton" id="button_take_photo">
<property name="visible">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="image">image_select_symbolic</property> <property name="image">image_take_photo</property>
<property name="tooltip-text" translatable="yes">Set selection as image</property> <property name="tooltip-text" translatable="yes">Take photo</property>
</object>
</child>
<child>
<object class="GtkButton" id="button_return_photo">
<property name="visible">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="image">image_return_photo</property>
<property name="tooltip-text" translatable="yes">Return</property>
</object>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">False</property>
<property name="position">2</property>
</packing> </packing>
</child> </child>
<!-- 3rd state: select photo area or return -->
<child> <child>
<object class="GtkButton" id="button_choose_picture"> <object class="GtkBox" id="button_box_edit">
<style>
<class name="linked"/>
</style>
<child>
<object class="GtkButton" id="button_set_avatar">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="image">image_add_symbolic</property> <property name="image">image_select_area</property>
<property name="tooltip-text" translatable="yes">Choose image from file</property> <property name="tooltip-text" translatable="yes">Set selection as image</property>
</object> </object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="button_export_avatar"> <object class="GtkButton" id="button_return_edit">
<property name="can_focus">True</property> <property name="visible">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="image">image_save_symbolic</property> <property name="halign">center</property>
<property name="tooltip-text" translatable="yes">Save image to file</property> <property name="valign">center</property>
<property name="image">image_return_edit</property>
<property name="tooltip-text" translatable="yes">Return</property>
</object>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">False</property>
<property name="position">4</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment