@@ -3,6 +3,9 @@ package com.squareup.workflow1.traceviewer.ui
3
3
import androidx.compose.foundation.background
4
4
import androidx.compose.foundation.border
5
5
import androidx.compose.foundation.clickable
6
+ import androidx.compose.foundation.hoverable
7
+ import androidx.compose.foundation.interaction.MutableInteractionSource
8
+ import androidx.compose.foundation.interaction.collectIsHoveredAsState
6
9
import androidx.compose.foundation.layout.Arrangement
7
10
import androidx.compose.foundation.layout.Box
8
11
import androidx.compose.foundation.layout.Column
@@ -14,9 +17,12 @@ import androidx.compose.foundation.shape.RoundedCornerShape
14
17
import androidx.compose.material.Text
15
18
import androidx.compose.runtime.Composable
16
19
import androidx.compose.runtime.LaunchedEffect
20
+ import androidx.compose.runtime.getValue
21
+ import androidx.compose.runtime.remember
17
22
import androidx.compose.ui.Alignment
18
23
import androidx.compose.ui.ExperimentalComposeUiApi
19
24
import androidx.compose.ui.Modifier
25
+ import androidx.compose.ui.draw.clip
20
26
import androidx.compose.ui.geometry.Offset
21
27
import androidx.compose.ui.graphics.Color
22
28
import androidx.compose.ui.input.pointer.PointerEventType
@@ -27,6 +33,7 @@ import androidx.compose.ui.layout.positionInRoot
27
33
import androidx.compose.ui.unit.dp
28
34
import androidx.compose.ui.unit.sp
29
35
import com.squareup.workflow1.traceviewer.model.Node
36
+ import com.squareup.workflow1.traceviewer.model.NodeState
30
37
import com.squareup.workflow1.traceviewer.model.NodeUpdate
31
38
32
39
/* *
@@ -286,44 +293,81 @@ private fun DrawNode(
286
293
onExpandToggle : (Node ) -> Unit ,
287
294
storeNodeLocation : (Node , Offset ) -> Unit
288
295
) {
296
+ val interactionSource = remember { MutableInteractionSource () }
297
+ val isHovered by interactionSource.collectIsHoveredAsState()
298
+
289
299
val nodeUpdate = NodeUpdate .create(
290
300
current = node,
291
301
past = nodePast,
292
302
isAffected = isAffected
293
303
)
294
304
295
- Box (
296
- modifier = Modifier
297
- .background(nodeUpdate.state.color)
298
- .clickable {
299
- onNodeSelect(nodeUpdate)
300
- }
301
- .onPointerEvent(PointerEventType .Press ) {
302
- if (it.buttons.isSecondaryPressed) {
303
- onExpandToggle(node)
305
+ Box {
306
+ Box (
307
+ modifier = Modifier
308
+ .hoverable(interactionSource)
309
+ .background(nodeUpdate.state.color)
310
+ .clickable {
311
+ onNodeSelect(nodeUpdate)
304
312
}
305
- }
306
- .padding(16 .dp)
307
- .onGloballyPositioned { coords ->
308
- val offsetToTopLeft = coords.positionInRoot()
309
- val offsetToCenter = Offset (
310
- x = offsetToTopLeft.x + coords.size.width / 2 ,
311
- y = offsetToTopLeft.y + coords.size.height / 2
312
- )
313
- storeNodeLocation(node, offsetToCenter)
314
- }
315
- ) {
316
- Column (horizontalAlignment = Alignment .CenterHorizontally ) {
317
- Row (
318
- verticalAlignment = Alignment .CenterVertically ,
319
- horizontalArrangement = Arrangement .spacedBy(4 .dp)
320
- ) {
321
- if (node.children.isNotEmpty()) {
322
- Text (text = if (isExpanded) " ▼" else " ▶" )
313
+ .onPointerEvent(PointerEventType .Press ) {
314
+ if (it.buttons.isSecondaryPressed) {
315
+ onExpandToggle(node)
316
+ }
323
317
}
324
- Text (text = node.name)
318
+ .padding(16 .dp)
319
+ .onGloballyPositioned { coords ->
320
+ val offsetToTopLeft = coords.positionInRoot()
321
+ val offsetToCenter = Offset (
322
+ x = offsetToTopLeft.x + coords.size.width / 2 ,
323
+ y = offsetToTopLeft.y + coords.size.height / 2
324
+ )
325
+ storeNodeLocation(node, offsetToCenter)
326
+ }
327
+ ) {
328
+ Column (horizontalAlignment = Alignment .CenterHorizontally ) {
329
+ Row (
330
+ verticalAlignment = Alignment .CenterVertically ,
331
+ horizontalArrangement = Arrangement .spacedBy(4 .dp)
332
+ ) {
333
+ if (node.children.isNotEmpty()) {
334
+ Text (text = if (isExpanded) " ▼" else " ▶" )
335
+ }
336
+ Text (text = node.name)
337
+ }
338
+ Text (text = " ID: ${node.id} " )
325
339
}
326
- Text (text = " ID: ${node.id} " )
327
340
}
341
+
342
+ if (isHovered) {
343
+ NodeTooltip (
344
+ nodeState = nodeUpdate.state,
345
+ modifier = Modifier
346
+ .align(Alignment .TopEnd )
347
+ .background(nodeUpdate.state.color)
348
+ )
349
+ }
350
+ }
351
+ }
352
+
353
+ /* *
354
+ * A tooltip that appears on hover showing the node state type
355
+ */
356
+ @Composable
357
+ private fun NodeTooltip (
358
+ nodeState : NodeState ,
359
+ modifier : Modifier = Modifier
360
+ ) {
361
+ Box (
362
+ modifier = modifier
363
+ .clip(RoundedCornerShape (4 .dp))
364
+ .background(Color .Black .copy(alpha = 0.3f ))
365
+ .padding(horizontal = 8 .dp, vertical = 4 .dp)
366
+ ) {
367
+ Text (
368
+ text = nodeState.name,
369
+ color = Color .White ,
370
+ fontSize = 12 .sp
371
+ )
328
372
}
329
373
}
0 commit comments