@@ -28,6 +28,7 @@ import kotlinx.coroutines.coroutineScope
2828 * @param pannable when set to true pan is enabled
2929 * @param rotatable when set to true rotation is enabled
3030 * @param limitPan limits pan to bounds of parent Composable. Using this flag prevents creating
31+ * @param fixedAspectRatio when set to true aspect ratio of overlay is fixed
3132 * empty space on sides or edges of parent
3233 */
3334class DynamicCropState internal constructor(
@@ -43,7 +44,8 @@ class DynamicCropState internal constructor(
4344 zoomable : Boolean ,
4445 pannable : Boolean ,
4546 rotatable : Boolean ,
46- limitPan : Boolean
47+ limitPan : Boolean ,
48+ private val fixedAspectRatio : Boolean ,
4749) : CropState(
4850 imageSize = imageSize,
4951 containerSize = containerSize,
@@ -86,7 +88,7 @@ class DynamicCropState internal constructor(
8688 override suspend fun updateProperties (cropProperties : CropProperties , forceUpdate : Boolean ) {
8789 handleSize = cropProperties.handleSize
8890 minOverlaySize = handleSize * 2
89-
91+
9092 super .updateProperties(cropProperties, forceUpdate)
9193 }
9294
@@ -137,13 +139,23 @@ class DynamicCropState internal constructor(
137139 minDimension = minOverlaySize,
138140 rectTemp = rectTemp,
139141 overlayRect = overlayRect,
140- change = change
142+ change = change,
143+ aspectRatio = getAspectRatio(),
144+ fixedAspectRatio = fixedAspectRatio,
141145 )
142146
143147 snapOverlayRectTo(newRect)
144148 }
145149 }
146150
151+ private fun getAspectRatio (): Float {
152+ return if (aspectRatio == AspectRatio .Unspecified ) {
153+ imageSize.width / imageSize.height.toFloat()
154+ } else {
155+ aspectRatio.value
156+ }
157+ }
158+
147159 override suspend fun onUp (change : PointerInputChange ) = coroutineScope {
148160 if (touchRegion != TouchRegion .None ) {
149161
@@ -351,7 +363,9 @@ class DynamicCropState internal constructor(
351363 minDimension : Float ,
352364 rectTemp : Rect ,
353365 overlayRect : Rect ,
354- change : PointerInputChange
366+ change : PointerInputChange ,
367+ aspectRatio : Float ,
368+ fixedAspectRatio : Boolean ,
355369 ): Rect {
356370
357371 val position = change.position
@@ -368,7 +382,15 @@ class DynamicCropState internal constructor(
368382 // Set position of top left while moving with top left handle and
369383 // limit position to not intersect other handles
370384 val left = screenPositionX.coerceAtMost(rectTemp.right - minDimension)
371- val top = screenPositionY.coerceAtMost(rectTemp.bottom - minDimension)
385+ val top = if (fixedAspectRatio) {
386+ // If aspect ratio is fixed we need to calculate top position based on
387+ // left position and aspect ratio
388+ val width = rectTemp.right - left
389+ val height = width / aspectRatio
390+ rectTemp.bottom - height
391+ } else {
392+ screenPositionY.coerceAtMost(rectTemp.bottom - minDimension)
393+ }
372394 Rect (
373395 left = left,
374396 top = top,
@@ -382,7 +404,15 @@ class DynamicCropState internal constructor(
382404 // Set position of top left while moving with bottom left handle and
383405 // limit position to not intersect other handles
384406 val left = screenPositionX.coerceAtMost(rectTemp.right - minDimension)
385- val bottom = screenPositionY.coerceAtLeast(rectTemp.top + minDimension)
407+ val bottom = if (fixedAspectRatio) {
408+ // If aspect ratio is fixed we need to calculate bottom position based on
409+ // left position and aspect ratio
410+ val width = rectTemp.right - left
411+ val height = width / aspectRatio
412+ rectTemp.top + height
413+ } else {
414+ screenPositionY.coerceAtLeast(rectTemp.top + minDimension)
415+ }
386416 Rect (
387417 left = left,
388418 top = rectTemp.top,
@@ -396,7 +426,15 @@ class DynamicCropState internal constructor(
396426 // Set position of top left while moving with top right handle and
397427 // limit position to not intersect other handles
398428 val right = screenPositionX.coerceAtLeast(rectTemp.left + minDimension)
399- val top = screenPositionY.coerceAtMost(rectTemp.bottom - minDimension)
429+ val top = if (fixedAspectRatio) {
430+ // If aspect ratio is fixed we need to calculate top position based on
431+ // right position and aspect ratio
432+ val width = right - rectTemp.left
433+ val height = width / aspectRatio
434+ rectTemp.bottom - height
435+ } else {
436+ screenPositionY.coerceAtMost(rectTemp.bottom - minDimension)
437+ }
400438
401439 Rect (
402440 left = rectTemp.left,
@@ -412,7 +450,15 @@ class DynamicCropState internal constructor(
412450 // Set position of top left while moving with bottom right handle and
413451 // limit position to not intersect other handles
414452 val right = screenPositionX.coerceAtLeast(rectTemp.left + minDimension)
415- val bottom = screenPositionY.coerceAtLeast(rectTemp.top + minDimension)
453+ val bottom = if (fixedAspectRatio) {
454+ // If aspect ratio is fixed we need to calculate bottom position based on
455+ // right position and aspect ratio
456+ val width = right - rectTemp.left
457+ val height = width / aspectRatio
458+ rectTemp.top + height
459+ } else {
460+ screenPositionY.coerceAtLeast(rectTemp.top + minDimension)
461+ }
416462
417463 Rect (
418464 left = rectTemp.left,
0 commit comments