@@ -7,7 +7,9 @@ import androidx.compose.foundation.layout.Box
7
7
import androidx.compose.foundation.layout.Column
8
8
import androidx.compose.foundation.layout.Row
9
9
import androidx.compose.foundation.layout.fillMaxSize
10
+ import androidx.compose.foundation.layout.fillMaxWidth
10
11
import androidx.compose.foundation.layout.padding
12
+ import androidx.compose.foundation.shape.RoundedCornerShape
11
13
import androidx.compose.material.Text
12
14
import androidx.compose.runtime.Composable
13
15
import androidx.compose.runtime.LaunchedEffect
@@ -26,6 +28,7 @@ import androidx.compose.ui.input.pointer.isPrimaryPressed
26
28
import androidx.compose.ui.input.pointer.isSecondaryPressed
27
29
import androidx.compose.ui.input.pointer.onPointerEvent
28
30
import androidx.compose.ui.unit.dp
31
+ import androidx.compose.ui.unit.sp
29
32
import com.squareup.moshi.JsonAdapter
30
33
import com.squareup.workflow1.traceviewer.TraceMode
31
34
import com.squareup.workflow1.traceviewer.model.Node
@@ -140,6 +143,7 @@ internal fun RenderTrace(
140
143
* A mutable map is used to persist the expansion state of the nodes, allowing them to be open and
141
144
* closed from user clicks.
142
145
*/
146
+ @OptIn(ExperimentalComposeUiApi ::class )
143
147
@Composable
144
148
private fun DrawTree (
145
149
node : Node ,
@@ -151,17 +155,20 @@ private fun DrawTree(
151
155
) {
152
156
Column (
153
157
modifier
154
- .padding(5 .dp)
155
- .border(1 .dp, Color .Black )
158
+ .padding(6 .dp)
159
+ .border(3 .dp, Color .Black )
156
160
.fillMaxSize(),
157
161
horizontalAlignment = Alignment .CenterHorizontally ,
158
162
) {
163
+ val groupKey = " ${node.id} -unaffected"
159
164
val isAffected = affectedNodes.contains(node)
160
165
// By default, nodes that relevant to this specific frame are expanded. All others are closed.
161
166
LaunchedEffect (expandedNodes) {
162
167
expandedNodes[node.id] = isAffected
168
+ expandedNodes[groupKey] = false
163
169
}
164
170
val isExpanded = expandedNodes[node.id] == true
171
+ val unaffectedChildrenExpanded = expandedNodes[groupKey] == true
165
172
166
173
DrawNode (
167
174
node = node,
@@ -174,26 +181,80 @@ private fun DrawTree(
174
181
175
182
// Draws the node's children recursively.
176
183
if (isExpanded) {
177
- Row (
178
- horizontalArrangement = Arrangement .Center ,
179
- verticalAlignment = Alignment .Top
180
- ) {
181
- /*
182
- We pair up the current node's children with previous frame's children.
183
- In the edge case that the current frame has additional children compared to the previous
184
- frame, we replace with null and will check before next recursive call.
185
- */
186
- node.children.forEach { (id, childNode) ->
187
- val prevFrameChildNode = previousFrameNode?.children?.get(id)
188
- DrawTree (
189
- node = childNode,
190
- previousFrameNode = prevFrameChildNode,
191
- affectedNodes = affectedNodes,
192
- expandedNodes = expandedNodes,
193
- onNodeSelect = onNodeSelect
194
- )
184
+ // Draw the affected children, and only draw the unaffected children it is clicked annd expanded.
185
+ val (affectedChildren, unaffectedChildren) = node.children.values
186
+ .partition { affectedNodes.contains(it) }
187
+
188
+
189
+ if (unaffectedChildren.isNotEmpty()) {
190
+ Box (
191
+ modifier = Modifier
192
+ .onPointerEvent(PointerEventType .Press ) {
193
+ if (it.buttons.isSecondaryPressed) {
194
+ // The open/close state for this group is always set when this node is first composed.
195
+ expandedNodes[groupKey] = ! expandedNodes[groupKey]!!
196
+ }
197
+ }
198
+ ) {
199
+ if (! unaffectedChildrenExpanded) {
200
+ Column (
201
+ modifier = Modifier
202
+ .background(Color .LightGray .copy(alpha = 0.3f ), shape = RoundedCornerShape (4 .dp))
203
+ .border(1 .dp, Color .Gray )
204
+ .padding(8 .dp),
205
+ horizontalAlignment = Alignment .CenterHorizontally
206
+ ) {
207
+ Text (text = " ${node.name} 's" , color = Color .DarkGray )
208
+ Text (text = " ${unaffectedChildren.size} unaffected children" , color = Color .DarkGray , fontSize = 12 .sp)
209
+ }
210
+ } else {
211
+ DrawChildren (
212
+ children = unaffectedChildren,
213
+ previousFrameNode = previousFrameNode,
214
+ affectedNodes = affectedNodes,
215
+ expandedNodes = expandedNodes,
216
+ onNodeSelect = onNodeSelect
217
+ )
218
+ }
195
219
}
196
220
}
221
+
222
+ if (affectedChildren.isNotEmpty()) {
223
+ DrawChildren (
224
+ children = affectedChildren,
225
+ previousFrameNode = previousFrameNode,
226
+ affectedNodes = affectedNodes,
227
+ expandedNodes = expandedNodes,
228
+ onNodeSelect = onNodeSelect
229
+ )
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ @Composable
236
+ private fun DrawChildren (
237
+ children : List <Node >,
238
+ previousFrameNode : Node ? ,
239
+ affectedNodes : Set <Node >,
240
+ expandedNodes : MutableMap <String , Boolean >,
241
+ onNodeSelect : (NodeUpdate ) -> Unit ,
242
+ ) {
243
+ Row (
244
+ modifier = Modifier
245
+ .fillMaxWidth()
246
+ .padding(8 .dp),
247
+ horizontalArrangement = Arrangement .SpaceEvenly ,
248
+ verticalAlignment = Alignment .Top
249
+ ) {
250
+ children.forEach { childNode ->
251
+ DrawTree (
252
+ node = childNode,
253
+ previousFrameNode = previousFrameNode?.children?.get(childNode.id),
254
+ affectedNodes = affectedNodes,
255
+ expandedNodes = expandedNodes,
256
+ onNodeSelect = onNodeSelect
257
+ )
197
258
}
198
259
}
199
260
}
@@ -227,7 +288,7 @@ private fun DrawNode(
227
288
onExpandToggle(node)
228
289
}
229
290
}
230
- .padding(10 .dp)
291
+ .padding(16 .dp)
231
292
) {
232
293
Column (horizontalAlignment = Alignment .CenterHorizontally ) {
233
294
Row (
0 commit comments