Skip to content

Commit 2943804

Browse files
add MorphSubcomposeLayout
1 parent a8318f3 commit 2943804

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.smarttoolfactory.image.transform
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.runtime.Composable
5+
import androidx.compose.ui.Modifier
6+
import androidx.compose.ui.layout.Placeable
7+
import androidx.compose.ui.layout.SubcomposeLayout
8+
import androidx.compose.ui.layout.SubcomposeMeasureScope
9+
import androidx.compose.ui.platform.LocalDensity
10+
import androidx.compose.ui.unit.Dp
11+
import androidx.compose.ui.unit.IntSize
12+
import androidx.compose.ui.unit.dp
13+
14+
/**
15+
* [SubcomposeLayout] layouts children like [Box] and returns [IntSize] of this Composable.
16+
* It doesn't layout mainContent because our [mainContent] and [dependentContent] are
17+
* combined as one Composable. We just measure exact size of initial dimensions
18+
* for a Composable to use it inside for setting size of Rectangles for initial bounds
19+
* for instance.
20+
*
21+
* @param handleRadius radius of handles on corners or sides to drag
22+
* @param updatePhysicalSize when we change size of a child composable whether it should
23+
* increase dimension of this Layout
24+
* @param mainContent is content that will be drawn and measured
25+
* with [SubcomposeMeasureScope.subcompose] to pass to [dependentContent] to create touch region
26+
* and overlay
27+
*/
28+
@Composable
29+
internal fun MorphSubcomposeLayout(
30+
modifier: Modifier = Modifier,
31+
handleRadius: Dp = 15.dp,
32+
updatePhysicalSize:Boolean =false,
33+
mainContent: @Composable () -> Unit,
34+
dependentContent: @Composable (IntSize) -> Unit
35+
) {
36+
37+
val handleRadiusInPx = with(LocalDensity.current) {
38+
handleRadius.roundToPx()
39+
}
40+
41+
SubcomposeLayout(modifier = modifier) { constraints ->
42+
43+
// Subcompose(compose only a section) main content and get Placeable
44+
val mainPlaceables: List<Placeable> = subcompose(SlotsEnum.Main, mainContent)
45+
.map {
46+
it.measure(constraints)
47+
}
48+
49+
// Get max width and height of main component
50+
var maxWidth = 0
51+
var maxHeight = 0
52+
53+
mainPlaceables.forEach { placeable: Placeable ->
54+
maxWidth += placeable.width
55+
maxHeight = placeable.height
56+
}
57+
58+
val handleSize = handleRadiusInPx * 2
59+
maxWidth = maxWidth.coerceAtMost(constraints.maxWidth - handleSize)
60+
maxHeight = maxHeight.coerceAtMost(constraints.maxHeight - handleSize)
61+
62+
val maxSize = IntSize(maxWidth, maxHeight)
63+
64+
val dependentPlaceables = subcompose(SlotsEnum.Dependent) {
65+
dependentContent(maxSize)
66+
}.map {
67+
it.measure(constraints)
68+
}
69+
70+
val dependentMaxSize =
71+
dependentPlaceables.fold(IntSize.Zero) { currentMax: IntSize, placeable: Placeable ->
72+
IntSize(
73+
width = maxOf(currentMax.width, placeable.width),
74+
height = maxOf(currentMax.height, placeable.height)
75+
)
76+
}
77+
78+
val width:Int
79+
val height:Int
80+
if (updatePhysicalSize){
81+
// Set sum of content and handle size as total size of this Composable
82+
width = dependentMaxSize.width
83+
height = dependentMaxSize.height
84+
85+
}else {
86+
// Set sum of dependent size, if it grows physical size of Composable also grows
87+
width = maxSize.width + 2 * handleRadiusInPx
88+
height = maxSize.height + 2 * handleRadiusInPx
89+
}
90+
91+
layout(width, height) {
92+
dependentPlaceables.forEach { placeable: Placeable ->
93+
placeable.placeRelative(0, 0)
94+
}
95+
}
96+
}
97+
}
98+
99+
enum class SlotsEnum { Main, Dependent }

0 commit comments

Comments
 (0)