[accounts-dialog] Make crop area more featureful



commit 61551b6da96c90a513e0a8eb6607b098eb6036fd
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Jan 26 02:08:40 2010 -0500

    Make crop area more featureful
    
    Copying the unconstrained cropping from Shotwell, and allowing to
    set minimal size and aspect ratio.

 src/um-crop-area.c    |  336 ++++++++++++++++++++++++++++++++++++++----------
 src/um-crop-area.h    |   15 ++-
 src/um-photo-dialog.c |    2 +
 3 files changed, 278 insertions(+), 75 deletions(-)
---
diff --git a/src/um-crop-area.c b/src/um-crop-area.c
index 0f67695..2c473a1 100644
--- a/src/um-crop-area.c
+++ b/src/um-crop-area.c
@@ -40,6 +40,9 @@ struct _UmCropAreaPrivate {
         gint active_region;
         gint last_press_x;
         gint last_press_y;
+        gint base_width;
+        gint base_height;
+        gdouble aspect;
 };
 
 G_DEFINE_TYPE (UmCropArea, um_crop_area, GTK_TYPE_DRAWING_AREA);
@@ -139,8 +142,8 @@ update_pixbufs (UmCropArea *area)
                 shift_colors (area->priv->color_shifted, -32, -32, -32, 0);
 
                 if (area->priv->scale == 0.0) {
-                        area->priv->crop.width = 96.0 / scale;
-                        area->priv->crop.height = 96.0 / scale;
+                        area->priv->crop.width = 2 * area->priv->base_width / scale;
+                        area->priv->crop.height = 2 * area->priv->base_height / scale;
                         area->priv->crop.x = (gdk_pixbuf_get_width (area->priv->browse_pixbuf) - area->priv->crop.width) / 2;
                         area->priv->crop.y = (gdk_pixbuf_get_height (area->priv->browse_pixbuf) - area->priv->crop.height) / 2;
                 }
@@ -363,10 +366,15 @@ update_cursor (UmCropArea *area,
 {
         gint cursor_type;
         GdkRectangle crop;
+        gint region;
 
-        crop_to_widget (area, &crop);
+        region = area->priv->active_region;
+        if (region == OUTSIDE) {
+                crop_to_widget (area, &crop);
+                region = find_location (&crop, x, y);
+        }
 
-        switch (find_location (&crop, x, y)) {
+        switch (region) {
         case OUTSIDE:
                 cursor_type = GDK_LEFT_PTR;
                 break;
@@ -407,110 +415,277 @@ update_cursor (UmCropArea *area,
         }
 }
 
-static gboolean 
+static int
+eval_radial_line (gdouble center_x, gdouble center_y,
+                  gdouble bounds_x, gdouble bounds_y,
+                  gdouble user_x)
+{
+        gdouble decision_slope;
+        gdouble decision_intercept;
+
+        decision_slope = (bounds_y - center_y) / (bounds_x - center_x);
+        decision_intercept = bounds_y = -(decision_slope * bounds_x);
+
+        return (int) (decision_slope * user_x + decision_intercept);
+}
+
+static gboolean
 um_crop_area_motion_notify_event (GtkWidget      *widget,
                                   GdkEventMotion *event)
 {
+        UmCropArea *area = UM_CROP_AREA (widget);
         gint x, y;
-        gint x2, y2;
         gint delta_x, delta_y;
-        gint width, height, d;
+        gint width, height;
+        gint adj_width, adj_height;
+        gint pb_width, pb_height;
         GdkRectangle damage;
-        GdkRectangle damage2;
-        UmCropArea *area = UM_CROP_AREA (widget);
+        gint left, right, top, bottom;
+        gdouble new_width, new_height;
+        gdouble center_x, center_y;
+        gint min_width, min_height;
 
         if (area->priv->browse_pixbuf == NULL)
                 return FALSE;
 
+        update_cursor (area, event->x, event->y);
+
         crop_to_widget (area, &damage);
+        gtk_widget_queue_draw_area (widget,
+                                    damage.x - 1, damage.y - 1,
+                                    damage.width + 2, damage.height + 2);
 
-        width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
-        height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
+        pb_width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
+        pb_height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
 
         x = (event->x - area->priv->image.x) / area->priv->scale;
         y = (event->y - area->priv->image.y) / area->priv->scale;
-        x = CLAMP (x, 0, width);
-        y = CLAMP (y, 0, height);
 
         delta_x = x - area->priv->last_press_x;
         delta_y = y - area->priv->last_press_y;
         area->priv->last_press_x = x;
         area->priv->last_press_y = y;
 
-        x2 = area->priv->crop.x + area->priv->crop.width;
-        y2 = area->priv->crop.y + area->priv->crop.height;
+        left = area->priv->crop.x;
+        right = area->priv->crop.x + area->priv->crop.width - 1;
+        top = area->priv->crop.y;
+        bottom = area->priv->crop.y + area->priv->crop.height - 1;
+
+        center_x = (left + right) / 2.0;
+        center_y = (top + bottom) / 2.0;
 
         switch (area->priv->active_region) {
         case INSIDE:
-                area->priv->crop.x = CLAMP (area->priv->crop.x + delta_x, 0, width - area->priv->crop.width);
-                area->priv->crop.y = CLAMP (area->priv->crop.y + delta_y, 0, height - area->priv->crop.height);
+                width = right - left + 1;
+                height = bottom - top + 1;
+
+                left += delta_x;
+                right += delta_x;
+                top += delta_y;
+                bottom += delta_y;
+
+                if (left < 0)
+                        left = 0;
+                if (top < 0)
+                        top = 0;
+                if (right > pb_width)
+                        right = pb_width;
+                if (bottom > pb_height)
+                        bottom = pb_height;
+
+                adj_width = right - left + 1;
+                adj_height = bottom - top + 1;
+                if (adj_width != width) {
+                        if (delta_x < 0)
+                                right = left + width - 1;
+                        else
+                                left = right - width + 1;
+                }
+                if (adj_height != height) {
+                        if (delta_y < 0)
+                                bottom = top + height - 1;
+                        else
+                                top = bottom - height + 1;
+                }
+
                 break;
 
         case TOP_LEFT:
-                d = MAX (x2 - x, y2 - y);
-                if (d < 48 / area->priv->scale)
-                        d = 48 / area->priv->scale;
-                if (x2 - d < 0)
-                        d = x2;
-                if (y2 - d < 0)
-                        d = y2;
-                area->priv->crop.x = x2 - d;
-                area->priv->crop.y = y2 - d;
-                area->priv->crop.width = area->priv->crop.height = d;
+                if (area->priv->aspect < 0) {
+                        top = y;
+                        left = x;
+                }
+                else if (y < eval_radial_line (center_x, center_y, left, top, x)) {
+                        top = y;
+                        new_width = (bottom - top) * area->priv->aspect;
+                        left = right - new_width;
+                }
+                else {
+                        left = x;
+                        new_height = (right - left) / area->priv->aspect;
+                        top = bottom - new_height;
+                }
                 break;
 
         case TOP:
+                top = y;
+                if (area->priv->aspect > 0) {
+                        new_width = (bottom - top) * area->priv->aspect;
+                        right = left + new_width;
+                }
+                break;
+
         case TOP_RIGHT:
-                d = MAX (y2 - y, x - area->priv->crop.x);
-                if (d < 48 / area->priv->scale)
-                        d = 48 / area->priv->scale;
-                if (area->priv->crop.x + d > width)
-                        d = width - area->priv->crop.x;
-                if (y2 - d < 0)
-                        d = y2;
-                area->priv->crop.y = y2 - d;
-                area->priv->crop.width = area->priv->crop.height = d;
+                if (area->priv->aspect < 0) {
+                        top = y;
+                        right = x;
+                }
+                else if (y < eval_radial_line (center_x, center_y, right, top, x)) {
+                        top = y;
+                        new_width = (bottom - top) * area->priv->aspect;
+                        right = left + new_width;
+                }
+                else {
+                        right = x;
+                        new_height = (right - left) / area->priv->aspect;
+                        top = bottom - new_height;
+                }
                 break;
 
         case LEFT:
+                left = x;
+                if (area->priv->aspect > 0) {
+                        new_height = (right - left) / area->priv->aspect;
+                        bottom = top + new_height;
+                }
+                break;
+
         case BOTTOM_LEFT:
-                d = MAX (x2 - x, y - area->priv->crop.y);
-                if (d < 48 / area->priv->scale)
-                        d = 48 / area->priv->scale;
-                if (area->priv->crop.y + d > height)
-                        d = height - area->priv->crop.y;
-                if (x2 - d < 0)
-                        d = x2;
-                area->priv->crop.x = x2 - d;
-                area->priv->crop.width = area->priv->crop.height = d;
+                if (area->priv->aspect < 0) {
+                        bottom = y;
+                        left = x;
+                }
+                else if (y < eval_radial_line (center_x, center_y, left, bottom, x)) {
+                        left = x;
+                        new_height = (right - left) / area->priv->aspect;
+                        bottom = top + new_height;
+                }
+                else {
+                        bottom = y;
+                        new_width = (bottom - top) * area->priv->aspect;
+                        left = right - new_width;
+                }
                 break;
 
         case RIGHT:
+                right = x;
+                if (area->priv->aspect > 0) {
+                        new_height = (right - left) / area->priv->aspect;
+                        bottom = top + new_height;
+                }
+                break;
+
         case BOTTOM_RIGHT:
-        case BOTTOM:
-                area->priv->crop.width = MAX (x - area->priv->crop.x, y - area->priv->crop.y);
-                if (area->priv->crop.width < 48 / area->priv->scale)
-                        area->priv->crop.width = 48 / area->priv->scale;
-                if (area->priv->crop.x + area->priv->crop.width > width)
-                        area->priv->crop.width = width - area->priv->crop.x;
-                area->priv->crop.height = area->priv->crop.width;
-                if (area->priv->crop.y + area->priv->crop.height > height)
-                        area->priv->crop.height = height - area->priv->crop.y;
-                area->priv->crop.width = area->priv->crop.height;
+                if (area->priv->aspect < 0) {
+                        bottom = y;
+                        right = x;
+                }
+                else if (y < eval_radial_line (center_x, center_y, right, bottom, x)) {
+                        right = x;
+                        new_height = (right - left) / area->priv->aspect;
+                        bottom = top + new_height;
+                }
+                else {
+                        bottom = y;
+                        new_width = (bottom - top) * area->priv->aspect;
+                        right = left + new_width;
+                }
                 break;
 
-        case OUTSIDE:
+        case BOTTOM:
+                bottom = y;
+                if (area->priv->aspect > 0) {
+                        new_width = (bottom - top) * area->priv->aspect;
+                        right= left + new_width;
+                }
                 break;
-        default: ;
+
+        default:
+                return FALSE;
+        }
+
+        min_width = area->priv->base_width / area->priv->scale;
+        min_height = area->priv->base_height / area->priv->scale;
+
+        width = right - left + 1;
+        height = bottom - top + 1;
+        if (area->priv->aspect < 0) {
+                if (left < 0)
+                        left = 0;
+                if (top < 0)
+                        top = 0;
+                if (right > pb_width)
+                        right = pb_width;
+                if (bottom > pb_height)
+                        bottom = pb_height;
+
+                width = right - left + 1;
+                height = bottom - top + 1;
+
+                switch (area->priv->active_region) {
+                case LEFT:
+                case TOP_LEFT:
+                case BOTTOM_LEFT:
+                        if (width < min_width)
+                                left = right - min_width;
+                        break;
+                case RIGHT:
+                case TOP_RIGHT:
+                case BOTTOM_RIGHT:
+                        if (width < min_width)
+                                right = left + min_width;
+                        break;
+
+                default: ;
+                }
+
+                switch (area->priv->active_region) {
+                case TOP:
+                case TOP_LEFT:
+                case TOP_RIGHT:
+                        if (height < min_height)
+                                top = bottom - min_height;
+                        break;
+                case BOTTOM:
+                case BOTTOM_LEFT:
+                case BOTTOM_RIGHT:
+                        if (height < min_height)
+                                bottom = top + min_height;
+                        break;
+
+                default: ;
+                }
+        }
+        else {
+                if (left < 0 || top < 0 ||
+                    right > pb_width || bottom > pb_height ||
+                    width < min_width || height < min_height) {
+                        left = area->priv->crop.x;
+                        right = area->priv->crop.x + area->priv->crop.width - 1;
+                        top = area->priv->crop.y;
+                        bottom = area->priv->crop.y + area->priv->crop.height - 1;
+                }
         }
 
-        crop_to_widget (area, &damage2);
-        gdk_rectangle_union (&damage, &damage2, &damage);
+        area->priv->crop.x = left;
+        area->priv->crop.y = top;
+        area->priv->crop.width = right - left + 1;
+        area->priv->crop.height = bottom - top + 1;
 
+        crop_to_widget (area, &damage);
         gtk_widget_queue_draw_area (widget,
                                     damage.x - 1, damage.y - 1,
                                     damage.width + 2, damage.height + 2);
-        update_cursor (area, event->x, event->y);
 
         return FALSE;
 }
@@ -611,6 +786,9 @@ um_crop_area_init (UmCropArea *area)
         area->priv->image.width = 0;
         area->priv->image.height = 0;
         area->priv->active_region = OUTSIDE;
+        area->priv->base_width = 48;
+        area->priv->base_height = 48;
+        area->priv->aspect = 1;
 }
 
 GtkWidget *
@@ -647,18 +825,11 @@ um_crop_area_set_picture (UmCropArea *area,
                 height = 0;
         }
 
-#if 0
-        gtk_widget_get_allocation (um->browse_drawing_area, &allocation);
-        um->priv->crop.width = 96;
-        um->priv->crop.height = 96;
-        um->priv->crop.x = (allocation.width - um->priv->crop.width) / 2;
-        um->priv->crop.y = (allocation.height - um->priv->crop.height) / 2;
-#else
-        area->priv->crop.width = 96;
-        area->priv->crop.height = 96;
+        area->priv->crop.width = 2 * area->priv->base_width;
+        area->priv->crop.height = 2 * area->priv->base_height;
         area->priv->crop.x = (width - area->priv->crop.width) / 2;
         area->priv->crop.y = (height - area->priv->crop.height) / 2;
-#endif
+
         area->priv->scale = 0.0;
         area->priv->image.x = 0;
         area->priv->image.y = 0;
@@ -668,3 +839,28 @@ um_crop_area_set_picture (UmCropArea *area,
         gtk_widget_queue_draw (GTK_WIDGET (area));
 }
 
+void
+um_crop_area_set_min_size (UmCropArea *area,
+                           gint        width,
+                           gint        height)
+{
+        area->priv->base_width = width;
+        area->priv->base_height = height;
+
+        if (area->priv->aspect > 0) {
+                area->priv->aspect = area->priv->base_width / (gdouble)area->priv->base_height;
+        }
+}
+
+void
+um_crop_area_set_constrain_aspect (UmCropArea *area,
+                                   gboolean    constrain)
+{
+        if (constrain) {
+                area->priv->aspect = area->priv->base_width / (gdouble)area->priv->base_height;
+        }
+        else {
+                area->priv->aspect = -1;
+        }
+}
+
diff --git a/src/um-crop-area.h b/src/um-crop-area.h
index 26b82b9..8992957 100644
--- a/src/um-crop-area.h
+++ b/src/um-crop-area.h
@@ -48,12 +48,17 @@ struct _UmCropArea {
         UmCropAreaPrivate *priv;
 };
 
-GType      um_crop_area_get_type    (void) G_GNUC_CONST;
+GType      um_crop_area_get_type             (void) G_GNUC_CONST;
 
-GtkWidget *um_crop_area_new         (void);
-GdkPixbuf *um_crop_area_get_picture (UmCropArea *area);
-void       um_crop_area_set_picture (UmCropArea *area,
-                                     GdkPixbuf  *pixbuf);
+GtkWidget *um_crop_area_new                  (void);
+GdkPixbuf *um_crop_area_get_picture          (UmCropArea *area);
+void       um_crop_area_set_picture          (UmCropArea *area,
+                                              GdkPixbuf  *pixbuf);
+void       um_crop_area_set_min_size         (UmCropArea *area,
+                                              gint        width,
+                                              gint        height);
+void       um_crop_area_set_constrain_aspect (UmCropArea *area,
+                                              gboolean    constrain);
 
 G_END_DECLS
 
diff --git a/src/um-photo-dialog.c b/src/um-photo-dialog.c
index 4e886be..81a4814 100644
--- a/src/um-photo-dialog.c
+++ b/src/um-photo-dialog.c
@@ -105,6 +105,8 @@ um_photo_dialog_crop (UmPhotoDialog *um,
 
         /* Content */
         um->crop_area           = um_crop_area_new ();
+        um_crop_area_set_min_size (UM_CROP_AREA (um->crop_area), 48, 48);
+        um_crop_area_set_constrain_aspect (UM_CROP_AREA (um->crop_area), TRUE);
         um_crop_area_set_picture (UM_CROP_AREA (um->crop_area), pixbuf);
         frame                   = gtk_frame_new (NULL);
         gtk_container_add (GTK_CONTAINER (frame), um->crop_area);



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