Skip to content

Instantly share code, notes, and snippets.

@orangy
Last active April 13, 2021 17:24
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orangy/e2d2a27201657e3eb921da9d45776b5a to your computer and use it in GitHub Desktop.
Save orangy/e2d2a27201657e3eb921da9d45776b5a to your computer and use it in GitHub Desktop.
fun Modifier.shape(width: Dp, strokeBrush: Brush, fillColor: Color, shape: Shape): Modifier = composed(
factory = {
this.then(
when {
shape === RectangleShape -> rectangleModifier(width, strokeBrush, fillColor)
else -> shapeModifier(width, strokeBrush, fillColor, shape)
}
)
},
inspectorInfo = debugInspectorInfo {
name = "shape"
properties["width"] = width
if (strokeBrush is SolidColor) {
properties["color"] = strokeBrush.value
value = strokeBrush.value
} else {
properties["brush"] = strokeBrush
}
properties["shape"] = shape
}
)
private fun rectangleModifier(width: Dp, strokeBrush: Brush, fillColor: Color) = Modifier.drawWithCache {
val strokeWidth = if (width == Dp.Hairline) 1f else width.toPx()
val stroke = Stroke(strokeWidth)
drawRectangleShape(stroke, strokeBrush, fillColor)
}
private fun CacheDrawScope.drawRectangleShape(stroke: Stroke, strokeBrush: Brush, fillColor: Color) =
onDrawWithContent {
val strokeWidth = stroke.width
val inset = strokeWidth / 2
if (fillColor != Color.Unspecified) {
drawRect(
color = fillColor,
topLeft = Offset(inset, inset),
size = Size(size.width - strokeWidth, size.height - strokeWidth),
style = Fill
)
}
drawContent()
drawRect(
brush = strokeBrush,
topLeft = Offset(inset, inset),
size = Size(size.width - strokeWidth, size.height - strokeWidth),
style = stroke
)
}
private fun CacheDrawScope.drawRoundedShape(
outline: Outline.Rounded,
inset: Float,
stroke: Stroke,
strokeBrush: Brush,
fillColor: Color
) =
onDrawWithContent {
when {
outline.roundRect.isSimple -> {
val roundRect = outline.roundRect
if (fillColor != Color.Unspecified) {
withTransform({ translate(inset, inset) }) {
drawRoundRect(
color = fillColor,
topLeft = Offset(roundRect.left, roundRect.top),
size = Size(roundRect.width, roundRect.height),
cornerRadius = roundRect.topLeftCornerRadius,
style = Fill
)
}
}
drawContent()
withTransform({ translate(inset, inset) }) {
drawRoundRect(
brush = strokeBrush,
topLeft = Offset(roundRect.left, roundRect.top),
size = Size(roundRect.width, roundRect.height),
cornerRadius = roundRect.topLeftCornerRadius,
style = stroke
)
}
}
else -> {
val path = Path().apply {
addRoundRect(outline.roundRect)
translate(Offset(inset, inset))
}
if (fillColor != Color.Unspecified) {
drawPath(path, color = fillColor, style = Fill)
}
drawContent()
drawPath(path, strokeBrush, style = stroke)
}
}
}
private fun CacheDrawScope.drawPathShape(path: Path, stroke: Stroke, strokeBrush: Brush, fillColor: Color) =
onDrawWithContent {
if (fillColor != Color.Unspecified) {
drawPath(path, color = fillColor, style = Fill)
}
drawContent()
drawPath(path, strokeBrush, style = stroke)
}
private fun shapeModifier(width: Dp, strokeBrush: Brush, fillColor: Color, shape: Shape) = Modifier.drawWithCache {
val strokeWidth = if (width == Dp.Hairline) 1f else width.toPx()
val inset = strokeWidth / 2
val insetSize = Size(size.width - inset * 2, size.height - inset * 2)
val stroke = Stroke(strokeWidth)
val outline: Outline = shape.createOutline(insetSize, layoutDirection, this)
when {
strokeWidth > 0 && size.minDimension > 0f -> when (outline) {
is Outline.Rectangle -> drawRectangleShape(stroke, strokeBrush, fillColor)
is Outline.Rounded -> drawRoundedShape(outline, inset, stroke, strokeBrush, fillColor)
is Outline.Generic -> {
val path = Path().apply { addPath(outline.path, Offset(inset, inset)) }
drawPathShape(path, stroke, strokeBrush, fillColor)
}
}
else -> onDrawWithContent {
drawContent()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment