Merge Modifier.contentBackdrop into Modifier.drawBackdrop

This commit is contained in:
Kyant0 committed 2025-09-24 11:00:05 +08:00
1 parent f23b9e5408
commit 00e9479e32
3 files changed
+91 -308

No files matched your search

@@ -1,264 +0,0 @@
package com.kyant.backdrop
import android.os.Build
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.GraphicsLayerScope
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.asComposeRenderEffect
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.LayoutModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.ObserverModifierNode
import androidx.compose.ui.node.invalidateDraw
import androidx.compose.ui.node.observeReads
import androidx.compose.ui.node.requireGraphicsContext
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntOffset
import com.kyant.backdrop.highlight.Highlight
import com.kyant.backdrop.highlight.HighlightElement
import com.kyant.backdrop.shadow.Shadow
import com.kyant.backdrop.shadow.ShadowElement
fun Modifier.contentBackdrop(
shape: () -> Shape,
highlight: (() -> Highlight?)? = DefaultHighlight,
shadow: (() -> Shadow?)? = DefaultShadow,
layer: (GraphicsLayerScope.() -> Unit)? = null,
drawContent: Boolean = false,
onDrawBehind: (DrawScope.() -> Unit)? = null,
onDrawSurface: (DrawScope.() -> Unit)? = null,
effects: BackdropEffectScope.() -> Unit
): Modifier {
val shapeProvider = ShapeProvider(shape)
return this
.then(
if (layer != null) {
Modifier.graphicsLayer(layer)
} else {
Modifier
}
)
.then(
if (shadow != null) {
ShadowElement(
shapeProvider = shapeProvider,
shadow = shadow
)
} else {
Modifier
}
)
.then(
if (highlight != null) {
HighlightElement(
shapeProvider = shapeProvider,
highlight = highlight
)
} else {
Modifier
}
)
.then(
ContentBackdropElement(
shapeProvider = shapeProvider,
layer = layer,
drawContent = drawContent,
onDrawBehind = onDrawBehind,
onDrawSurface = onDrawSurface,
effects = effects
)
)
}
private class ContentBackdropElement(
val shapeProvider: ShapeProvider,
val layer: (GraphicsLayerScope.() -> Unit)?,
val drawContent: Boolean,
val onDrawBehind: (DrawScope.() -> Unit)?,
val onDrawSurface: (DrawScope.() -> Unit)?,
val effects: BackdropEffectScope.() -> Unit
) : ModifierNodeElement<ContentBackdropNode>() {
override fun create(): ContentBackdropNode {
return ContentBackdropNode(
shapeProvider = shapeProvider,
layer = layer,
drawContent = drawContent,
onDrawBehind = onDrawBehind,
onDrawSurface = onDrawSurface,
effects = effects
)
}
override fun update(node: ContentBackdropNode) {
node.shapeProvider = shapeProvider
node.layer = layer
node.drawContent = drawContent
node.onDrawBehind = onDrawBehind
node.onDrawSurface = onDrawSurface
node.effects = effects
node.invalidateDrawCache()
}
override fun InspectorInfo.inspectableProperties() {
name = "contentBackdrop"
properties["shapeProvider"] = shapeProvider
properties["layer"] = layer
properties["drawContent"] = drawContent
properties["onDrawBehind"] = onDrawBehind
properties["onDrawSurface"] = onDrawSurface
properties["effects"] = effects
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ContentBackdropElement) return false
if (shapeProvider != other.shapeProvider) return false
if (layer != other.layer) return false
if (drawContent != other.drawContent) return false
if (onDrawBehind != other.onDrawBehind) return false
if (onDrawSurface != other.onDrawSurface) return false
if (effects != other.effects) return false
return true
}
override fun hashCode(): Int {
var result = shapeProvider.hashCode()
result = 31 * result + (layer?.hashCode() ?: 0)
result = 31 * result + drawContent.hashCode()
result = 31 * result + (onDrawBehind?.hashCode() ?: 0)
result = 31 * result + (onDrawSurface?.hashCode() ?: 0)
result = 31 * result + effects.hashCode()
return result
}
}
private class ContentBackdropNode(
var shapeProvider: ShapeProvider,
var layer: (GraphicsLayerScope.() -> Unit)?,
var drawContent: Boolean,
var onDrawBehind: (DrawScope.() -> Unit)?,
var onDrawSurface: (DrawScope.() -> Unit)?,
var effects: BackdropEffectScope.() -> Unit
) : LayoutModifierNode, DrawModifierNode, ObserverModifierNode, Modifier.Node() {
override val shouldAutoInvalidate: Boolean = false
private var graphicsLayer: GraphicsLayer? = null
private val effectScope =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
object : BackdropEffectScopeImpl() {
override val shape: Shape get() = shapeProvider.innerShape
}
} else {
null
}
private val layerBlock: GraphicsLayerScope.() -> Unit = {
clip = true
shape = shapeProvider.shape
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
}
private var inverseLayerScope: InverseLayerScope? = null
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val placeable = measurable.measure(constraints)
return layout(placeable.width, placeable.height) {
placeable.placeWithLayer(IntOffset.Zero, layerBlock = layerBlock)
}
}
override fun ContentDrawScope.draw() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (effectScope != null && effectScope.update(this)) {
updateEffects()
}
}
onDrawBehind?.invoke(this)
val layer = graphicsLayer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && layer != null) {
layer.record { this@draw.drawContent() }
val layerBlock = this@ContentBackdropNode.layer
if (layerBlock != null) {
withTransform({
val inverseLayerScope =
inverseLayerScope?.apply { reset() }
?: InverseLayerScope().also { inverseLayerScope = it }
with(inverseLayerScope) { inverseTransform(this@draw, layerBlock) }
}) {
drawLayer(layer)
}
} else {
drawLayer(layer)
}
if (drawContent) {
drawContent()
}
} else {
drawContent()
}
onDrawSurface?.invoke(this)
}
override fun onObservedReadsChanged() {
invalidateDrawCache()
}
fun invalidateDrawCache() {
observeEffects()
invalidateDraw()
}
private fun observeEffects() {
if (effectScope != null) {
observeReads { updateEffects() }
}
}
private fun updateEffects() {
if (effectScope != null) {
effectScope.renderEffect = null
effects(effectScope)
graphicsLayer?.renderEffect = effectScope.renderEffect?.asComposeRenderEffect()
}
}
override fun onAttach() {
val graphicsContext = requireGraphicsContext()
graphicsLayer = graphicsContext.createGraphicsLayer()
observeEffects()
}
override fun onDetach() {
val graphicsContext = requireGraphicsContext()
graphicsLayer?.let { layer ->
graphicsContext.releaseGraphicsLayer(layer)
graphicsLayer = null
}
inverseLayerScope = null
effectScope?.reset()
}
}
@@ -25,21 +25,21 @@ import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.node.ObserverModifierNode
import androidx.compose.ui.node.invalidateDraw
import androidx.compose.ui.node.observeReads
import androidx.compose.ui.node.requireDensity
import androidx.compose.ui.node.requireGraphicsContext
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.toIntSize
import com.kyant.backdrop.backdrops.LayerBackdrop
import com.kyant.backdrop.highlight.Highlight
import com.kyant.backdrop.highlight.HighlightElement
import com.kyant.backdrop.shadow.Shadow
import com.kyant.backdrop.shadow.ShadowElement
internal val DefaultHighlight = { Highlight.Default }
internal val DefaultShadow = { Shadow.Default }
private val DefaultHighlight = { Highlight.Default }
private val DefaultShadow = { Shadow.Default }
private val DefaultOnDrawBackdrop: DrawScope.(DrawScope.() -> Unit) -> Unit = { it() }
private val DefaultOnDrawContent: ContentDrawScope.() -> Unit = { drawContent() }
fun Modifier.drawBackdrop(
backdrop: Backdrop,
@@ -51,8 +51,11 @@ fun Modifier.drawBackdrop(
onDrawBehind: (DrawScope.() -> Unit)? = null,
onDrawBackdrop: DrawScope.(drawBackdrop: DrawScope.() -> Unit) -> Unit = DefaultOnDrawBackdrop,
onDrawSurface: (DrawScope.() -> Unit)? = null,
onDrawContent: ContentDrawScope.() -> Unit = DefaultOnDrawContent,
onDrawFront: (DrawScope.() -> Unit)? = null,
effects: BackdropEffectScope.() -> Unit
drawContent: Boolean = true,
contentEffects: (BackdropEffectScope.() -> Unit)? = null,
effects: (BackdropEffectScope.() -> Unit)? = null
): Modifier {
val shapeProvider = ShapeProvider(shape)
return this
@@ -92,7 +95,10 @@ fun Modifier.drawBackdrop(
onDrawBehind = onDrawBehind,
onDrawBackdrop = onDrawBackdrop,
onDrawSurface = onDrawSurface,
onDrawContent = onDrawContent,
onDrawFront = onDrawFront,
drawContent = drawContent,
contentEffects = contentEffects,
effects = effects
)
)
@@ -106,8 +112,11 @@ private class DrawBackdropElement(
val onDrawBehind: (DrawScope.() -> Unit)?,
val onDrawBackdrop: DrawScope.(drawBackdrop: DrawScope.() -> Unit) -> Unit,
val onDrawSurface: (DrawScope.() -> Unit)?,
val onDrawContent: ContentDrawScope.() -> Unit,
val onDrawFront: (DrawScope.() -> Unit)?,
val effects: BackdropEffectScope.() -> Unit
val drawContent: Boolean,
val contentEffects: (BackdropEffectScope.() -> Unit)?,
val effects: (BackdropEffectScope.() -> Unit)?
) : ModifierNodeElement<DrawBackdropNode>() {
override fun create(): DrawBackdropNode {
@@ -119,7 +128,10 @@ private class DrawBackdropElement(
onDrawBehind = onDrawBehind,
onDrawBackdrop = onDrawBackdrop,
onDrawSurface = onDrawSurface,
onDrawContent = onDrawContent,
onDrawFront = onDrawFront,
drawContent = drawContent,
contentEffects = contentEffects,
effects = effects
)
}
@@ -132,7 +144,10 @@ private class DrawBackdropElement(
node.onDrawBehind = onDrawBehind
node.onDrawBackdrop = onDrawBackdrop
node.onDrawSurface = onDrawSurface
node.onDrawContent = onDrawContent
node.onDrawFront = onDrawFront
node.drawContent = drawContent
node.contentEffects = contentEffects
node.effects = effects
node.invalidateDrawCache()
}
@@ -146,7 +161,10 @@ private class DrawBackdropElement(
properties["onDrawBehind"] = onDrawBehind
properties["onDrawBackdrop"] = onDrawBackdrop
properties["onDrawSurface"] = onDrawSurface
properties["onDrawContent"] = onDrawContent
properties["onDrawFront"] = onDrawFront
properties["drawContent"] = drawContent
properties["contentEffects"] = contentEffects
properties["effects"] = effects
}
@@ -161,7 +179,10 @@ private class DrawBackdropElement(
if (onDrawBehind != other.onDrawBehind) return false
if (onDrawBackdrop != other.onDrawBackdrop) return false
if (onDrawSurface != other.onDrawSurface) return false
if (onDrawContent != other.onDrawContent) return false
if (onDrawFront != other.onDrawFront) return false
if (drawContent != other.drawContent) return false
if (contentEffects != other.contentEffects) return false
if (effects != other.effects) return false
return true
@@ -175,7 +196,10 @@ private class DrawBackdropElement(
result = 31 * result + (onDrawBehind?.hashCode() ?: 0)
result = 31 * result + onDrawBackdrop.hashCode()
result = 31 * result + (onDrawSurface?.hashCode() ?: 0)
result = 31 * result + onDrawContent.hashCode()
result = 31 * result + (onDrawFront?.hashCode() ?: 0)
result = 31 * result + drawContent.hashCode()
result = 31 * result + (contentEffects?.hashCode() ?: 0)
result = 31 * result + effects.hashCode()
return result
}
@@ -189,19 +213,24 @@ private class DrawBackdropNode(
var onDrawBehind: (DrawScope.() -> Unit)?,
var onDrawBackdrop: DrawScope.(drawBackdrop: DrawScope.() -> Unit) -> Unit,
var onDrawSurface: (DrawScope.() -> Unit)?,
var onDrawContent: ContentDrawScope.() -> Unit,
var onDrawFront: (DrawScope.() -> Unit)?,
var effects: BackdropEffectScope.() -> Unit
var drawContent: Boolean,
var contentEffects: (BackdropEffectScope.() -> Unit)?,
var effects: (BackdropEffectScope.() -> Unit)?
) : LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode, ObserverModifierNode, Modifier.Node() {
override val shouldAutoInvalidate: Boolean = false
private var graphicsLayer: GraphicsLayer? = null
private var contentGraphicsLayer: GraphicsLayer? = null
private var layoutCoordinates: LayoutCoordinates? by mutableStateOf(null, neverEqualPolicy())
private val recordBlock: (DrawScope.() -> Unit) = {
onDrawBackdrop {
with(backdrop) {
drawBackdrop(
density = mutableDensity,
density = requireDensity(),
coordinates = layoutCoordinates,
layerBlock = layer
)
@@ -225,13 +254,6 @@ private class DrawBackdropNode(
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
}
private val mutableDensity = object : Density {
override var density: Float = 1f
override var fontScale: Float = 1f
}
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
@@ -253,14 +275,7 @@ private class DrawBackdropNode(
val layer = graphicsLayer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && layer != null) {
mutableDensity.density = density
mutableDensity.fontScale = fontScale
layer.record(
density = mutableDensity,
layoutDirection = layoutDirection,
size = size.toIntSize(),
block = recordBlock
)
layer.record(block = recordBlock)
drawLayer(layer)
} else {
recordBlock()
@@ -268,15 +283,20 @@ private class DrawBackdropNode(
onDrawSurface?.invoke(this)
drawContent()
val contentLayer = contentGraphicsLayer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && contentLayer != null) {
contentLayer.record { this@draw.onDrawContent() }
drawLayer(contentLayer)
}
if (drawContent) {
onDrawContent()
}
onDrawFront?.invoke(this)
exportedBackdrop?.graphicsLayer?.record(
density = mutableDensity,
layoutDirection = layoutDirection,
size = size.toIntSize()
) {
exportedBackdrop?.graphicsLayer?.record {
onDrawBehind?.invoke(this)
val layer = graphicsLayer
@@ -322,15 +342,44 @@ private class DrawBackdropNode(
private fun updateEffects() {
if (effectScope != null) {
effectScope.renderEffect = null
effects(effectScope)
graphicsLayer?.renderEffect = effectScope.renderEffect?.asComposeRenderEffect()
val graphicsContext = requireGraphicsContext()
val backdropEffects = effects
if (backdropEffects != null) {
if (graphicsLayer == null) {
graphicsLayer = graphicsContext.createGraphicsLayer()
}
effectScope.renderEffect = null
backdropEffects(effectScope)
graphicsLayer?.renderEffect = effectScope.renderEffect?.asComposeRenderEffect()
} else {
graphicsLayer?.let { layer ->
graphicsContext.releaseGraphicsLayer(layer)
graphicsLayer = null
}
}
val contentEffects = contentEffects
if (contentEffects != null) {
if (contentGraphicsLayer == null) {
val graphicsContext = requireGraphicsContext()
contentGraphicsLayer = graphicsContext.createGraphicsLayer()
}
effectScope.renderEffect = null
contentEffects(effectScope)
contentGraphicsLayer?.renderEffect = effectScope.renderEffect?.asComposeRenderEffect()
} else {
contentGraphicsLayer?.let { layer ->
graphicsContext.releaseGraphicsLayer(layer)
contentGraphicsLayer = null
}
}
}
}
override fun onAttach() {
val graphicsContext = requireGraphicsContext()
graphicsLayer = graphicsContext.createGraphicsLayer()
observeEffects()
}
@@ -340,6 +389,10 @@ private class DrawBackdropNode(
graphicsContext.releaseGraphicsLayer(layer)
graphicsLayer = null
}
contentGraphicsLayer?.let { layer ->
graphicsContext.releaseGraphicsLayer(layer)
contentGraphicsLayer = null
}
layoutCoordinates = null
effectScope?.reset()
}
@@ -48,7 +48,6 @@ import com.kyant.backdrop.catalog.BackdropDemoScaffold
import com.kyant.backdrop.catalog.R
import com.kyant.backdrop.catalog.utils.ProgressConverter
import com.kyant.backdrop.catalog.utils.rememberUISensor
import com.kyant.backdrop.contentBackdrop
import com.kyant.backdrop.drawBackdrop
import com.kyant.backdrop.effects.blur
import com.kyant.backdrop.effects.refraction
@@ -208,17 +207,12 @@ fun ControlCenterContent() {
highlight = null,
shadow = null,
onDrawSurface = glassSurface,
contentEffects = {
blur(8f.dp.toPx())
refraction(8f.dp.toPx(), size.minDimension / 4f, true)
},
effects = glassEffects
)
.contentBackdrop(
{ ContinuousRoundedRectangle(40f.dp) },
highlight = glassHighlight,
shadow = null,
drawContent = true
) {
blur(8f.dp.toPx())
refraction(8f.dp.toPx(), size.minDimension / 4f, true)
}
.size(itemTwoSpanSize)
.padding(itemSpacing)
) {