Created
April 6, 2023 14:55
-
-
Save Peanuuutz/ec414e2cd8f49934620f91e6cd5f358e to your computer and use it in GitHub Desktop.
Compose Border with Effect
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@file:OptIn(ExperimentalComposeUiApi::class) | |
package net.peanuuutz.compose.desktop | |
import androidx.compose.runtime.Stable | |
import androidx.compose.ui.ExperimentalComposeUiApi | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.geometry.Offset | |
import androidx.compose.ui.geometry.Size | |
import androidx.compose.ui.graphics.Brush | |
import androidx.compose.ui.graphics.PathEffect | |
import androidx.compose.ui.graphics.RectangleShape | |
import androidx.compose.ui.graphics.Shape | |
import androidx.compose.ui.graphics.drawscope.ContentDrawScope | |
import androidx.compose.ui.graphics.drawscope.DrawScope | |
import androidx.compose.ui.graphics.drawscope.DrawStyle | |
import androidx.compose.ui.graphics.drawscope.Fill | |
import androidx.compose.ui.graphics.drawscope.Stroke | |
import androidx.compose.ui.node.DrawModifierNode | |
import androidx.compose.ui.node.ModifierNodeElement | |
import androidx.compose.ui.unit.Dp | |
import kotlin.math.ceil | |
import kotlin.math.min | |
/** | |
* Adds a border to the modified element with appearance specified with a | |
* [width], a [brush], an [effect] and a [shape]. The border will be inside the | |
* bounds of the element. | |
* | |
* @param width width of the border. Use [Dp.Hairline] for a hairline border. | |
* @param brush brush to paint the border with. | |
* @param effect [PathEffect] applied to the border stroke. | |
* @param shape shape of the border. | |
*/ | |
@Stable | |
fun Modifier.borderWithEffect( | |
width: Dp, | |
brush: Brush, | |
effect: PathEffect?, | |
shape: Shape = RectangleShape | |
): Modifier { | |
val element = BorderWithEffectElement( | |
width = width, | |
brush = brush, | |
effect = effect, | |
shape = shape | |
) | |
return this then element | |
} | |
// ======== Internal ======== | |
private data class BorderWithEffectElement( | |
val width: Dp, | |
val brush: Brush, | |
val effect: PathEffect?, | |
val shape: Shape | |
) : ModifierNodeElement<BorderWithEffectNode>() { | |
override fun create(): BorderWithEffectNode { | |
return BorderWithEffectNode( | |
width = width, | |
brush = brush, | |
effect = effect, | |
shape = shape | |
) | |
} | |
override fun update(node: BorderWithEffectNode): BorderWithEffectNode { | |
node.width = width | |
node.brush = brush | |
node.effect = effect | |
node.shape = shape | |
return node | |
} | |
} | |
private class BorderWithEffectNode( | |
var width: Dp, | |
var brush: Brush, | |
var effect: PathEffect?, | |
var shape: Shape // TODO Implementation | |
) : Modifier.Node(), DrawModifierNode { | |
override fun ContentDrawScope.draw() { | |
drawContent() | |
drawBorder() | |
} | |
private fun DrawScope.drawBorder() { | |
val definedStrokeWidth = width | |
val canvasSize = size | |
val canvasMinDimension = canvasSize.minDimension | |
if (definedStrokeWidth.toPx() < 0.0f || canvasMinDimension <= 0.0f) { | |
return | |
} | |
val resolvedDefinedStrokeWidth = if (definedStrokeWidth != Dp.Hairline) { | |
definedStrokeWidth.toPx() | |
} else { | |
1.0f | |
} | |
val strokeWidth = min(resolvedDefinedStrokeWidth, ceil(canvasMinDimension / 2)) | |
val borderTopLeft: Offset | |
val borderSize: Size | |
val style: DrawStyle | |
if (strokeWidth * 2 <= canvasMinDimension) { | |
borderTopLeft = Offset( | |
x = strokeWidth / 2, | |
y = strokeWidth / 2 | |
) | |
borderSize = Size( | |
width = canvasSize.width - strokeWidth, | |
height = canvasSize.height - strokeWidth | |
) | |
style = Stroke( | |
width = strokeWidth, | |
pathEffect = effect | |
) | |
} else { | |
borderTopLeft = Offset.Zero | |
borderSize = canvasSize | |
style = Fill | |
} | |
drawRect( | |
brush = brush, | |
topLeft = borderTopLeft, | |
size = borderSize, | |
style = style | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment