Skip to content

Commit 7a03fb4

Browse files
add EnhancedZoomModifier overload functions
1 parent e386be4 commit 7a03fb4

File tree

2 files changed

+234
-3
lines changed

2 files changed

+234
-3
lines changed

image/src/main/java/com/smarttoolfactory/image/zoom/EnhancedZoomModifier.kt

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,234 @@ fun Modifier.enhancedZoom(
112112
inspectorInfo = {
113113
name = "enhancedZoom"
114114
// add name and value of each argument
115+
properties["key"] = key
116+
properties["consume"] = consume
117+
properties["clip"] = clip
118+
properties["onDown"] = onGestureStart
119+
properties["onMove"] = onGesture
120+
properties["onUp"] = onGestureEnd
121+
}
122+
)
123+
124+
125+
/**
126+
* Modifier that zooms in or out of Composable set to. This zoom modifier has option
127+
* to move back to bounds with an animation or option to have fling gesture when user removes
128+
* from screen while velocity is higher than threshold to have smooth touch effect.
129+
*
130+
* [key1], [key2] are used for [Modifier.pointerInput] to restart closure when any keys assigned
131+
* change
132+
* @param consume flag to prevent other gestures such as scroll, drag or transform to get
133+
* @param clip when set to true clips to parent bounds. Anything outside parent bounds is not
134+
* drawn
135+
* empty space on sides or edges of parent.
136+
* @param enhancedZoomState State of the zoom that contains option to set initial, min, max zoom,
137+
* enabling rotation, pan or zoom and contains current [ZoomData]
138+
* event propagations
139+
* @param onGestureStart callback to to notify gesture has started and return current
140+
* [EnhancedZoomData] of this modifier
141+
* @param onGesture callback to notify about ongoing gesture and return current
142+
* [EnhancedZoomData] of this modifier
143+
* @param onGestureEnd callback to notify that gesture finished return current
144+
* [EnhancedZoomData] of this modifier
145+
*/
146+
fun Modifier.enhancedZoom(
147+
key1: Any?,
148+
key2: Any?,
149+
consume: Boolean = true,
150+
clip: Boolean = true,
151+
enhancedZoomState: EnhancedZoomState,
152+
onGestureStart: ((EnhancedZoomData) -> Unit)? = null,
153+
onGesture: ((EnhancedZoomData) -> Unit)? = null,
154+
onGestureEnd: ((EnhancedZoomData) -> Unit)? = null,
155+
) = composed(
156+
157+
factory = {
158+
159+
val coroutineScope = rememberCoroutineScope()
160+
161+
val boundPan = enhancedZoomState.limitPan && !enhancedZoomState.rotationEnabled
162+
val clipToBounds = (clip || boundPan)
163+
164+
val transformModifier = Modifier.pointerInput(key1,key2) {
165+
// Pass size of this Composable this Modifier is attached for constraining operations
166+
// inside this bounds
167+
enhancedZoomState.size = this.size
168+
detectTransformGestures(
169+
consume = consume,
170+
onGestureStart = {
171+
onGestureStart?.invoke(enhancedZoomState.enhancedZoomData)
172+
},
173+
onGestureEnd = {
174+
coroutineScope.launch {
175+
enhancedZoomState.onGestureEnd {
176+
onGestureEnd?.invoke(enhancedZoomState.enhancedZoomData)
177+
}
178+
}
179+
},
180+
onGesture = { centroid, pan, zoom, rotate, mainPointer, pointerList ->
181+
182+
coroutineScope.launch {
183+
enhancedZoomState.onGesture(
184+
centroid = centroid,
185+
pan = pan,
186+
zoom = zoom,
187+
rotation = rotate,
188+
mainPointer = mainPointer,
189+
changes = pointerList
190+
)
191+
}
192+
193+
onGesture?.invoke(enhancedZoomState.enhancedZoomData)
194+
}
195+
)
196+
}
197+
198+
val tapModifier = Modifier.pointerInput(key1,key2) {
199+
// Pass size of this Composable this Modifier is attached for constraining operations
200+
// inside this bounds
201+
enhancedZoomState.size = this.size
202+
detectTapGestures(
203+
onDoubleTap = {
204+
coroutineScope.launch {
205+
enhancedZoomState.onDoubleTap {
206+
onGestureEnd?.invoke(enhancedZoomState.enhancedZoomData)
207+
}
208+
}
209+
}
210+
)
211+
}
212+
213+
val graphicsModifier = Modifier.graphicsLayer {
214+
this.update(enhancedZoomState)
215+
}
216+
217+
this.then(
218+
(if (clipToBounds) Modifier.clipToBounds() else Modifier)
219+
.then(tapModifier)
220+
.then(transformModifier)
221+
.then(graphicsModifier)
222+
)
223+
},
224+
inspectorInfo = {
225+
name = "enhancedZoom"
226+
// add name and value of each argument
227+
properties["key1"] = key1
228+
properties["key2"] = key2
229+
properties["consume"] = consume
230+
properties["clip"] = clip
231+
properties["onDown"] = onGestureStart
232+
properties["onMove"] = onGesture
233+
properties["onUp"] = onGestureEnd
234+
}
235+
)
236+
237+
238+
239+
/**
240+
* Modifier that zooms in or out of Composable set to. This zoom modifier has option
241+
* to move back to bounds with an animation or option to have fling gesture when user removes
242+
* from screen while velocity is higher than threshold to have smooth touch effect.
243+
*
244+
* @param keys are used for [Modifier.pointerInput] to restart closure when any keys assigned
245+
* change
246+
* @param consume flag to prevent other gestures such as scroll, drag or transform to get
247+
* @param clip when set to true clips to parent bounds. Anything outside parent bounds is not
248+
* drawn
249+
* empty space on sides or edges of parent.
250+
* @param enhancedZoomState State of the zoom that contains option to set initial, min, max zoom,
251+
* enabling rotation, pan or zoom and contains current [ZoomData]
252+
* event propagations
253+
* @param onGestureStart callback to to notify gesture has started and return current
254+
* [EnhancedZoomData] of this modifier
255+
* @param onGesture callback to notify about ongoing gesture and return current
256+
* [EnhancedZoomData] of this modifier
257+
* @param onGestureEnd callback to notify that gesture finished return current
258+
* [EnhancedZoomData] of this modifier
259+
*/
260+
fun Modifier.enhancedZoom(
261+
vararg keys: Any?,
262+
consume: Boolean = true,
263+
clip: Boolean = true,
264+
enhancedZoomState: EnhancedZoomState,
265+
onGestureStart: ((EnhancedZoomData) -> Unit)? = null,
266+
onGesture: ((EnhancedZoomData) -> Unit)? = null,
267+
onGestureEnd: ((EnhancedZoomData) -> Unit)? = null,
268+
) = composed(
269+
270+
factory = {
271+
272+
val coroutineScope = rememberCoroutineScope()
273+
274+
val boundPan = enhancedZoomState.limitPan && !enhancedZoomState.rotationEnabled
275+
val clipToBounds = (clip || boundPan)
276+
277+
val transformModifier = Modifier.pointerInput(keys) {
278+
// Pass size of this Composable this Modifier is attached for constraining operations
279+
// inside this bounds
280+
enhancedZoomState.size = this.size
281+
detectTransformGestures(
282+
consume = consume,
283+
onGestureStart = {
284+
onGestureStart?.invoke(enhancedZoomState.enhancedZoomData)
285+
},
286+
onGestureEnd = {
287+
coroutineScope.launch {
288+
enhancedZoomState.onGestureEnd {
289+
onGestureEnd?.invoke(enhancedZoomState.enhancedZoomData)
290+
}
291+
}
292+
},
293+
onGesture = { centroid, pan, zoom, rotate, mainPointer, pointerList ->
294+
295+
coroutineScope.launch {
296+
enhancedZoomState.onGesture(
297+
centroid = centroid,
298+
pan = pan,
299+
zoom = zoom,
300+
rotation = rotate,
301+
mainPointer = mainPointer,
302+
changes = pointerList
303+
)
304+
}
305+
306+
onGesture?.invoke(enhancedZoomState.enhancedZoomData)
307+
}
308+
)
309+
}
310+
311+
val tapModifier = Modifier.pointerInput(keys) {
312+
// Pass size of this Composable this Modifier is attached for constraining operations
313+
// inside this bounds
314+
enhancedZoomState.size = this.size
315+
detectTapGestures(
316+
onDoubleTap = {
317+
coroutineScope.launch {
318+
enhancedZoomState.onDoubleTap {
319+
onGestureEnd?.invoke(enhancedZoomState.enhancedZoomData)
320+
}
321+
}
322+
}
323+
)
324+
}
325+
326+
val graphicsModifier = Modifier.graphicsLayer {
327+
this.update(enhancedZoomState)
328+
}
329+
330+
this.then(
331+
(if (clipToBounds) Modifier.clipToBounds() else Modifier)
332+
.then(tapModifier)
333+
.then(transformModifier)
334+
.then(graphicsModifier)
335+
)
336+
},
337+
inspectorInfo = {
338+
name = "enhancedZoom"
339+
// add name and value of each argument
340+
properties["keys"] = keys
341+
properties["consume"] = consume
342+
properties["clip"] = clip
115343
properties["onDown"] = onGestureStart
116344
properties["onMove"] = onGesture
117345
properties["onUp"] = onGestureEnd

image/src/main/java/com/smarttoolfactory/image/zoom/ZoomableImage.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ import com.smarttoolfactory.image.ImageWithConstraints
1616
* Zoomable image that zooms in and out in [ [minZoom], [maxZoom] ] interval and translates
1717
* zoomed image based on pointer position.
1818
* Double tap gestures reset image translation and zoom to default values with animation.
19-
*
2019
* @param initialZoom zoom set initially
2120
* @param minZoom minimum zoom value this Composable can possess
2221
* @param maxZoom maximum zoom value this Composable can possess
22+
* @param clip whether image should be clip to bounds of Image
2323
* @param clipTransformToContentScale when set true zoomable image takes borders of image drawn
2424
* while zooming in. [contentScale] determines whether will be empty spaces on edges of Composable
2525
* @param limitPan limits pan to bounds of parent Composable. Using this flag prevents creating
@@ -53,6 +53,7 @@ fun ZoomableImage(
5353
zoomEnabled: Boolean = true,
5454
panEnabled: Boolean = true,
5555
rotationEnabled: Boolean = false,
56+
clip: Boolean = true,
5657
clipTransformToContentScale: Boolean = false,
5758
consume: Boolean = true,
5859
onGestureStart: (ZoomData) -> Unit = {},
@@ -76,6 +77,7 @@ fun ZoomableImage(
7677
rotationEnabled = rotationEnabled,
7778
),
7879
consume = consume,
80+
clip = clip,
7981
onGestureStart = onGestureStart,
8082
onGesture = onGesture,
8183
onGestureEnd = onGestureEnd
@@ -112,8 +114,7 @@ fun ZoomableImage(
112114
* Zoomable image that zooms in and out in [zoomState.minZoom, zoomState.maxZoom] interval and translates
113115
* zoomed image based on pointer position.
114116
* Double tap gestures reset image translation and zoom to default values with animation.
115-
*
116-
117+
* @param clip whether image should be clip to bounds of Image
117118
* @param clipTransformToContentScale when set true zoomable image takes borders of image drawn
118119
* while zooming in. [contentScale] determines whether will be empty spaces on edges of Composable
119120
* @param consume flag to prevent other gestures such as scroll, drag or transform to get
@@ -135,6 +136,7 @@ fun ZoomableImage(
135136
alpha: Float = DefaultAlpha,
136137
colorFilter: ColorFilter? = null,
137138
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
139+
clip: Boolean = true,
138140
clipTransformToContentScale: Boolean = false,
139141
zoomState: ZoomState,
140142
consume: Boolean = true,
@@ -150,6 +152,7 @@ fun ZoomableImage(
150152
key2 = contentScale,
151153
zoomState = zoomState,
152154
consume = consume,
155+
clip = clip,
153156
onGestureStart = onGestureStart,
154157
onGesture = onGesture,
155158
onGestureEnd = onGestureEnd

0 commit comments

Comments
 (0)