Hi I'm experiencing weird drawing behavior. I hav...
# compose
a
Hi I'm experiencing weird drawing behavior. I have four circled corners on a crop box, and I want to add a wider resizing circle on the corner when dragging. I could do that, but still, when I shrink the box, the resizing circle goes wrong. Am I missing anything related to parent constraints or something else?
🧵 1
Here is the crop box code:
Copy code
Box(
    modifier = Modifier
        .absolutePosition(x = offsetX, y = offsetY)
        .requiredSize(
            DpSize(
                abs(width.value).dp,
                abs(height.value).dp,
            ),
        ) // this affects offset if reached max size constraint
        .graphicsLayer {
            // rotate around center
            transformOrigin = TransformOrigin.Center
            rotationZ = state.rotation
        },
) {
    val lineColor = MaterialTheme.colorScheme.customColors.primary
    val sideGrabSize = DpSize(10.dp, 30.dp)
    Canvas(modifier = Modifier.fillMaxSize()) {
        drawLine(
            color = lineColor,
            start = Offset(0f, 0f),
            end = Offset((width).toPx(), 0f),
            strokeWidth = 1.dp.toPx(),
        )

        drawLine(
            color = lineColor,
            start = Offset(0f, (height).toPx()),
            end = Offset((width).toPx(), (height).toPx()),
            strokeWidth = 1.dp.toPx(),
        )

        drawLine(
            color = lineColor,
            start = Offset(0f, 0f),
            end = Offset(0f, (height).toPx()),
            strokeWidth = 1.dp.toPx(),
        )

        drawLine(
            color = lineColor,
            start = Offset((width).toPx(), 0f),
            end = Offset((width).toPx(), (height).toPx()),
            strokeWidth = 1.dp.toPx(),
        )

        // Draw corner handles
        if (cornerGrabsEnabled) {
            val topRight = Offset(bottomRight.x, topLeft.y)
            val bottomLeft = Offset(topLeft.x, bottomRight.y)

            if (isCornersIntersects || state.isResizing){
                val cornerPoint = when (state.lastCornerResized) {
                    Grab.TopLeft -> topLeft
                    Grab.TopRight -> topRight
                    Grab.BottomLeft -> bottomLeft
                    Grab.BottomRight -> bottomRight
                    else -> null
                }
                cornerPoint?.let {
                    drawShadowCircle(drawContext.canvas, center = it)
                }
            } else {
                listOf(topLeft, topRight, bottomLeft, bottomRight).forEach {
                    drawShadowCircle(drawContext.canvas, center = it)
                }
            }
        }
    }

    // ******************************************************************************
    // # CORNER GRABS
    // ******************************************************************************

    if (cornerGrabsEnabled) {
        val hotAreaSizePx = with(density) { hotAreaSize.toPx() }
        val halfHotAreaSize = hotAreaSizePx.div(2)

        val topLeftNew = Offset(topLeft.x - halfHotAreaSize, topLeft.y - halfHotAreaSize)
        val bottomRightNew = Offset(bottomRight.x - halfHotAreaSize, bottomRight.y - halfHotAreaSize)
        val topRightNew = Offset(bottomRight.x - halfHotAreaSize, topLeft.y - halfHotAreaSize)
        val bottomLeftNew = Offset(topLeft.x - halfHotAreaSize, bottomRight.y - halfHotAreaSize)

        // Check if any hot area intersects with the other
        isCornersIntersects = checkPointsIntersect(
            listOf(topLeftNew, bottomRightNew, topRightNew, bottomLeftNew),
            hotAreaSizePx
        )

        // Top-left corner grab hot area
        val topLeftCorner = if (isRtl) Grab.TopRight else Grab.TopLeft
        GrabHotArea(
            center = topLeftNew,
            hotAreaSize = hotAreaSize,
            grab = topLeftCorner,
            resizingGrab = state.lastCornerResized,
            isResizing = state.isResizing,
            canvasScale = canvasScale,
            isHotAreaEnabled = !isCornersIntersects || state.lastCornerResized == topLeftCorner,
            onEvent = onEvent,
        )

        // Bottom-right corner grab
        val bottomLeftCorner = if (isRtl) Grab.BottomLeft else Grab.BottomRight
        GrabHotArea(
            center = bottomRightNew,
            hotAreaSize = hotAreaSize,
            grab = bottomLeftCorner,
            resizingGrab = state.lastCornerResized,
            canvasScale = canvasScale,
            isResizing = state.isResizing,
            isHotAreaEnabled = !isCornersIntersects || state.lastCornerResized == bottomLeftCorner,
            onEvent = onEvent,
        )

        // Top-right corner grab
        val topRightCorner = if (isRtl) Grab.TopLeft else Grab.TopRight
        GrabHotArea(
            center = topRightNew,
            hotAreaSize = hotAreaSize,
            grab = topRightCorner,
            resizingGrab = state.lastCornerResized,
            canvasScale = canvasScale,
            isResizing = state.isResizing,
            isHotAreaEnabled = !isCornersIntersects || state.lastCornerResized == topRightCorner,
            onEvent = onEvent,
        )

        // Bottom-Left corner grab
        val bottomRightCorner = if (isRtl) Grab.BottomRight else Grab.BottomLeft
        GrabHotArea(
            center = bottomLeftNew,
            hotAreaSize = hotAreaSize,
            grab = bottomRightCorner,
            resizingGrab = state.lastCornerResized,
            canvasScale = canvasScale,
            isResizing = state.isResizing,
            isHotAreaEnabled = !isCornersIntersects || state.lastCornerResized == bottomRightCorner,
            onEvent = onEvent,
        )
    }

}
This composable is responsible for showing resizing circle around the corner:
Copy code
@Composable
private fun GrabHotArea(
    center: Offset, // in pixels
    hotAreaSize: Dp,
    grab: Grab,
    resizingGrab: Grab?,
    canvasScale: Float,
    isResizing: Boolean = false,
    isHotAreaEnabled: Boolean = true,
    onEvent: (CanvasEvent) -> Unit,
) {
    if (!isHotAreaEnabled) return

    val showCircle = grab.isCornerGrab && grab == resizingGrab && isResizing

    val scale by animateFloatAsState(
        targetValue = if (showCircle) 1f else 0f,
        animationSpec = tween(durationMillis = 250),
        label = "circleScale"
    )
    Box(
        modifier = Modifier
            .wrapContentSize(unbounded = true)
            .size(hotAreaSize)
            .offset {
                center.toIntOffset()
            }
            .pointerInput(canvasScale) {
                detectDragGestures(
                    onDrag = { _, dragAmount ->
                        onEvent(
                            CanvasEvent.SelectionViewGrabDragged(
                                grab = grab,
                                dragAmount = dragAmount.div(density).div(canvasScale),
                            ),
                        )
                    },
                    onDragEnd = {
                        onEvent(
                            CanvasEvent.SelectionViewGrabDragEnded(
                                grab = grab,
                            ),
                        )
                    }
                )
            },
    ){
        if (scale > 0f) {
            Box(
                modifier = Modifier
                    .matchParentSize()
                    .graphicsLayer {
                        scaleX = scale
                        scaleY = scale
                        alpha = scale
                    }
                    .background(
                        color = CustomColors.primary.copy(alpha = 0.5f),
                        shape = CircleShape
                    )
            )
        }
    }
}