Skip to content

Commit dceb9e4

Browse files
committed
Create sandbox-type background for app
- Allows users to freely zoom and pan to view different sections of the tree
1 parent 7bf649c commit dceb9e4

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.squareup.workflow1.traceviewer
2+
3+
import androidx.compose.foundation.gestures.awaitEachGesture
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.wrapContentSize
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.runtime.getValue
8+
import androidx.compose.runtime.mutableStateOf
9+
import androidx.compose.runtime.remember
10+
import androidx.compose.runtime.setValue
11+
import androidx.compose.ui.Modifier
12+
import androidx.compose.ui.geometry.Offset
13+
import androidx.compose.ui.graphics.graphicsLayer
14+
import androidx.compose.ui.input.pointer.PointerEventType
15+
import androidx.compose.ui.input.pointer.pointerInput
16+
17+
/**
18+
* This is the backdrop for the whole app. Since there can be hundreds of modules at a time, there
19+
* is not realistic way to fit everything on the screen at once. Having the liberty to pan across
20+
* the whole tree as well as zoom into specific subtrees means there's a lot more control when
21+
* analyzing the traces.
22+
*
23+
*/
24+
@Composable
25+
public fun SandboxBackground(content: @Composable () -> Unit) {
26+
var scale by remember { mutableStateOf(1f) }
27+
var offset by remember { mutableStateOf(Offset.Zero) }
28+
29+
Box(
30+
modifier = Modifier
31+
.wrapContentSize(unbounded = true) // this allows the content to be larger than the initial screen of the app
32+
.pointerInput(Unit) { // this allows for user's panning to view different parts of content
33+
awaitEachGesture {
34+
val event = awaitPointerEvent()
35+
36+
// zooming
37+
if (event.type == PointerEventType.Scroll) {
38+
val scrollDelta = event.changes.first().scrollDelta.y
39+
scale *= if (scrollDelta < 0) 1.1f else 0.9f
40+
scale = scale.coerceIn(0.1f, 10f)
41+
event.changes.forEach { it.consume() }
42+
}
43+
44+
// panning: this tracks multiple events within one gesture to see what the user is doing, then calculates the offset and pans the screen accordingly
45+
val drag = event.changes.firstOrNull()
46+
if (drag != null && drag.pressed) {
47+
var prev = drag.position
48+
while (true) {
49+
val nextEvent = awaitPointerEvent()
50+
val nextDrag = nextEvent.changes.firstOrNull() ?: break
51+
if (!nextDrag.pressed) break
52+
53+
val delta = nextDrag.position - prev
54+
offset += delta
55+
prev = nextDrag.position
56+
nextDrag.consume()
57+
}
58+
}
59+
}
60+
}
61+
.graphicsLayer {
62+
translationX = offset.x
63+
translationY = offset.y
64+
scaleX = scale
65+
scaleY = scale
66+
}
67+
) {
68+
Box {
69+
content() // this is main content
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)