Skip to content

Commit 378bed9

Browse files
Merge pull request #18 from Nimrodda/min-dimension
Added minimum dimension crop property
2 parents a13aa40 + 428c01e commit 378bed9

File tree

3 files changed

+26
-19
lines changed

3 files changed

+26
-19
lines changed

cropper/src/main/java/com/smarttoolfactory/cropper/settings/CropDefaults.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ object CropDefaults {
3939
zoomable: Boolean = true,
4040
rotatable: Boolean = false,
4141
fixedAspectRatio: Boolean = false,
42-
requiredSize: IntSize? = null
42+
requiredSize: IntSize? = null,
43+
minDimension: IntSize? = null,
4344
): CropProperties {
4445
return CropProperties(
4546
cropType = cropType,
@@ -54,7 +55,8 @@ object CropDefaults {
5455
zoomable = zoomable,
5556
rotatable = rotatable,
5657
fixedAspectRatio = fixedAspectRatio,
57-
requiredSize = requiredSize
58+
requiredSize = requiredSize,
59+
minDimension = minDimension,
5860
)
5961
}
6062

@@ -100,7 +102,8 @@ data class CropProperties internal constructor(
100102
val zoomable: Boolean,
101103
val maxZoom: Float,
102104
val fixedAspectRatio: Boolean = false,
103-
val requiredSize: IntSize? = null
105+
val requiredSize: IntSize? = null,
106+
val minDimension: IntSize? = null,
104107
)
105108

106109
/**

cropper/src/main/java/com/smarttoolfactory/cropper/state/CropState.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ fun rememberCropState(
2929

3030
// Properties of crop state
3131
val handleSize = cropProperties.handleSize
32-
val minOverlaySize = handleSize * 2
3332
val cropType = cropProperties.cropType
3433
val aspectRatio = cropProperties.aspectRatio
3534
val overlayRatio = cropProperties.overlayRatio
@@ -39,6 +38,7 @@ fun rememberCropState(
3938
val pannable = cropProperties.pannable
4039
val rotatable = cropProperties.rotatable
4140
val fixedAspectRatio = cropProperties.fixedAspectRatio
41+
val minDimension = cropProperties.minDimension
4242

4343
return remember(*keys) {
4444
when (cropType) {
@@ -67,13 +67,13 @@ fun rememberCropState(
6767
overlayRatio = overlayRatio,
6868
maxZoom = maxZoom,
6969
handleSize = handleSize,
70-
minOverlaySize = minOverlaySize,
7170
fling = fling,
7271
zoomable = zoomable,
7372
pannable = pannable,
7473
rotatable = rotatable,
7574
limitPan = true,
7675
fixedAspectRatio = fixedAspectRatio,
76+
minDimension = minDimension,
7777
)
7878
}
7979
}

cropper/src/main/java/com/smarttoolfactory/cropper/state/DynamicCropState.kt

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ import com.smarttoolfactory.cropper.TouchRegion
1010
import com.smarttoolfactory.cropper.model.AspectRatio
1111
import com.smarttoolfactory.cropper.settings.CropProperties
1212
import kotlinx.coroutines.coroutineScope
13+
import kotlin.math.roundToInt
1314

1415
/**
1516
* State for cropper with dynamic overlay. Overlay of this state can be moved or resized
1617
* using handles or touching inner position of overlay. When overlay overflow out of image bounds
1718
* or moves out of bounds it animates back to valid size and position
1819
*
1920
* @param handleSize size of the handle to control, move or scale dynamic overlay
20-
* @param minOverlaySize minimum overlay size that can be shrunk to by moving handles
2121
* @param imageSize size of the **Bitmap**
2222
* @param containerSize size of the Composable that draws **Bitmap**
2323
* @param maxZoom maximum zoom value
@@ -29,11 +29,11 @@ import kotlinx.coroutines.coroutineScope
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
3131
* @param fixedAspectRatio when set to true aspect ratio of overlay is fixed
32+
* @param minDimension minimum size of the overlay, if null defaults to handleSize * 2
3233
* empty space on sides or edges of parent
3334
*/
3435
class DynamicCropState internal constructor(
3536
private var handleSize: Float,
36-
private var minOverlaySize: Float,
3737
imageSize: IntSize,
3838
containerSize: IntSize,
3939
drawAreaSize: IntSize,
@@ -46,6 +46,7 @@ class DynamicCropState internal constructor(
4646
rotatable: Boolean,
4747
limitPan: Boolean,
4848
private val fixedAspectRatio: Boolean,
49+
private val minDimension: IntSize?
4950
) : CropState(
5051
imageSize = imageSize,
5152
containerSize = containerSize,
@@ -87,8 +88,6 @@ class DynamicCropState internal constructor(
8788

8889
override suspend fun updateProperties(cropProperties: CropProperties, forceUpdate: Boolean) {
8990
handleSize = cropProperties.handleSize
90-
minOverlaySize = handleSize * 2
91-
9291
super.updateProperties(cropProperties, forceUpdate)
9392
}
9493

@@ -131,12 +130,17 @@ class DynamicCropState internal constructor(
131130

132131
val change = changes.first()
133132

133+
// Default min dimension is handle size * 2
134+
val doubleHandleSize = handleSize * 2
135+
val defaultMinDimension =
136+
IntSize(doubleHandleSize.roundToInt(), doubleHandleSize.roundToInt())
137+
134138
// update overlay rectangle based on where its touched and touch position to corners
135139
// This function moves and/or scales overlay rectangle
136140
val newRect = updateOverlayRect(
137141
distanceToEdgeFromTouch = distanceToEdgeFromTouch,
138142
touchRegion = touchRegion,
139-
minDimension = minOverlaySize,
143+
minDimension = minDimension ?: defaultMinDimension,
140144
rectTemp = rectTemp,
141145
overlayRect = overlayRect,
142146
change = change,
@@ -359,7 +363,7 @@ class DynamicCropState internal constructor(
359363
private fun updateOverlayRect(
360364
distanceToEdgeFromTouch: Offset,
361365
touchRegion: TouchRegion,
362-
minDimension: Float,
366+
minDimension: IntSize,
363367
rectTemp: Rect,
364368
overlayRect: Rect,
365369
change: PointerInputChange,
@@ -380,15 +384,15 @@ class DynamicCropState internal constructor(
380384

381385
// Set position of top left while moving with top left handle and
382386
// limit position to not intersect other handles
383-
val left = screenPositionX.coerceAtMost(rectTemp.right - minDimension)
387+
val left = screenPositionX.coerceAtMost(rectTemp.right - minDimension.width)
384388
val top = if (fixedAspectRatio) {
385389
// If aspect ratio is fixed we need to calculate top position based on
386390
// left position and aspect ratio
387391
val width = rectTemp.right - left
388392
val height = width / aspectRatio
389393
rectTemp.bottom - height
390394
} else {
391-
screenPositionY.coerceAtMost(rectTemp.bottom - minDimension)
395+
screenPositionY.coerceAtMost(rectTemp.bottom - minDimension.height)
392396
}
393397
Rect(
394398
left = left,
@@ -402,15 +406,15 @@ class DynamicCropState internal constructor(
402406

403407
// Set position of top left while moving with bottom left handle and
404408
// limit position to not intersect other handles
405-
val left = screenPositionX.coerceAtMost(rectTemp.right - minDimension)
409+
val left = screenPositionX.coerceAtMost(rectTemp.right - minDimension.width)
406410
val bottom = if (fixedAspectRatio) {
407411
// If aspect ratio is fixed we need to calculate bottom position based on
408412
// left position and aspect ratio
409413
val width = rectTemp.right - left
410414
val height = width / aspectRatio
411415
rectTemp.top + height
412416
} else {
413-
screenPositionY.coerceAtLeast(rectTemp.top + minDimension)
417+
screenPositionY.coerceAtLeast(rectTemp.top + minDimension.height)
414418
}
415419
Rect(
416420
left = left,
@@ -424,15 +428,15 @@ class DynamicCropState internal constructor(
424428

425429
// Set position of top left while moving with top right handle and
426430
// limit position to not intersect other handles
427-
val right = screenPositionX.coerceAtLeast(rectTemp.left + minDimension)
431+
val right = screenPositionX.coerceAtLeast(rectTemp.left + minDimension.width)
428432
val top = if (fixedAspectRatio) {
429433
// If aspect ratio is fixed we need to calculate top position based on
430434
// right position and aspect ratio
431435
val width = right - rectTemp.left
432436
val height = width / aspectRatio
433437
rectTemp.bottom - height
434438
} else {
435-
screenPositionY.coerceAtMost(rectTemp.bottom - minDimension)
439+
screenPositionY.coerceAtMost(rectTemp.bottom - minDimension.height)
436440
}
437441

438442
Rect(
@@ -448,15 +452,15 @@ class DynamicCropState internal constructor(
448452

449453
// Set position of top left while moving with bottom right handle and
450454
// limit position to not intersect other handles
451-
val right = screenPositionX.coerceAtLeast(rectTemp.left + minDimension)
455+
val right = screenPositionX.coerceAtLeast(rectTemp.left + minDimension.width)
452456
val bottom = if (fixedAspectRatio) {
453457
// If aspect ratio is fixed we need to calculate bottom position based on
454458
// right position and aspect ratio
455459
val width = right - rectTemp.left
456460
val height = width / aspectRatio
457461
rectTemp.top + height
458462
} else {
459-
screenPositionY.coerceAtLeast(rectTemp.top + minDimension)
463+
screenPositionY.coerceAtLeast(rectTemp.top + minDimension.height)
460464
}
461465

462466
Rect(

0 commit comments

Comments
 (0)