diff --git a/fluent/src/commonMain/kotlin/com/konyaco/fluent/background/Layer.kt b/fluent/src/commonMain/kotlin/com/konyaco/fluent/background/Layer.kt index 2f3e0d7f..d7481eeb 100644 --- a/fluent/src/commonMain/kotlin/com/konyaco/fluent/background/Layer.kt +++ b/fluent/src/commonMain/kotlin/com/konyaco/fluent/background/Layer.kt @@ -20,7 +20,9 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.translate import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.ShaderBrush import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection @@ -28,6 +30,8 @@ import androidx.compose.ui.unit.dp import com.konyaco.fluent.FluentTheme import com.konyaco.fluent.LocalContentColor import com.konyaco.fluent.ProvideTextStyle +import kotlin.math.ceil +import kotlin.math.floor import kotlin.math.sqrt /** @@ -121,7 +125,17 @@ private fun Modifier.layer( if (border != null) { val backgroundShape = if (backgroundSizing == BackgroundSizing.InnerBorderEdge && shape is CornerBasedShape) { - BackgroundPaddingShape(shape) + val brush = border.brush + // FIXME(Workaround): If the border color has alpha channel, shrink padding to not cover the content background + val hasAlpha = if (brush is SolidColor) { + brush.value.alpha < 1f + } else if (brush is ShaderBrush) { + // TODO: Detect if there are transparent colors in gradient. + true + } else { + false + } + BackgroundPaddingShape(shape, cover = !hasAlpha) } else { shape } @@ -138,16 +152,18 @@ private fun Modifier.layer( * keep padding for background */ @Immutable -@JvmInline -private value class BackgroundPaddingShape(private val borderShape: CornerBasedShape) : Shape { +private class BackgroundPaddingShape( + private val borderShape: CornerBasedShape, + private val cover: Boolean +) : Shape { override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline { return with(density) { - val circular = borderShape == CircleShape + val circular = borderShape == CircleShape && size.height == size.width val paddingPx = when { - circular -> calcCircularPadding(density) - else -> calcPadding(density) - }.toPx() + circular -> calcCircularPadding(density, cover) + else -> calcPadding(density, cover) + } createInnerOutline(size, density, layoutDirection, paddingPx) } } @@ -202,28 +218,25 @@ private value class BackgroundPaddingShape(private val borderShape: CornerBasedS } } -/** - * This is a workaround solution to eliminate 1 pixel gap - * when density is not integer or `(density % 1) < 0.5` - */ @Stable -private fun calcPadding(density: Density): Dp { - val remainder = density.density % 1f - +private fun calcPadding(density: Density, cover: Boolean): Float { return with(density) { - when { - remainder == 0f -> 1.dp - else -> (1.dp.toPx() - remainder + 1).toDp() - } + if (cover) ceil(1.dp.toPx()) - 0.5f // cover + else ceil(1.dp.toPx())// do not cover } } @Stable -private fun calcCircularPadding(density: Density): Dp { +private fun calcCircularPadding(density: Density, cover: Boolean): Float { val remainder = density.density % 1f return with(density) { - if (remainder == 0f) 1.dp - else (1.dp.toPx() - remainder + 1).toDp() + if (remainder == 0f) { + if (cover) floor(1.dp.toPx()) - 0.5f // cover + else floor(1.dp.toPx()) - 0.15f// do not cover + } else { + if (cover) floor(1.dp.toPx()) // overlap + else floor(1.dp.toPx()) + 0.85f// do not cover + } } } \ No newline at end of file diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/settings/SettingsScreen.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/settings/SettingsScreen.kt index 45a4c433..051daa55 100644 --- a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/settings/SettingsScreen.kt +++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/settings/SettingsScreen.kt @@ -340,7 +340,52 @@ private fun Content() { }, backgroundSizing = BackgroundSizing.OuterBorderEdge ) - + Layer( + modifier = Modifier.size(32.dp), + shape = CircleShape, + color = FluentTheme.colors.fillAccent.default, + border = BorderStroke(1.dp, FluentTheme.colors.fillAccent.default), + backgroundSizing = BackgroundSizing.InnerBorderEdge, + content = {} + ) + Layer( + shape = CircleShape, + color = FluentTheme.colors.fillAccent.default, + border = BorderStroke(1.dp, FluentTheme.colors.fillAccent.default), + content = { Box(Modifier.size(height = 32.dp, width = 64.dp))}, + backgroundSizing = BackgroundSizing.InnerBorderEdge + ) + Layer( + shape = CircleShape, +// modifier = Modifier.size(32.dp), + color = FluentTheme.colors.controlStrong.default, + border = BorderStroke(1.dp, FluentTheme.colors.controlStrong.default), + backgroundSizing = BackgroundSizing.InnerBorderEdge, + content = { Box(Modifier.size(32.dp)) } + ) + Layer( + shape = CircleShape, + modifier = Modifier.size(64.dp, 32.dp), + color = FluentTheme.colors.controlStrong.default, + border = BorderStroke(1.dp, FluentTheme.colors.controlStrong.default), + backgroundSizing = BackgroundSizing.InnerBorderEdge, + content = {} + ) + Layer( + shape = RoundedCornerShape(4.dp), + color = FluentTheme.colors.fillAccent.default, + border = BorderStroke(1.dp, FluentTheme.colors.fillAccent.default), + content = { Box(Modifier.size(height = 32.dp, width = 64.dp))}, + backgroundSizing = BackgroundSizing.InnerBorderEdge + ) + Layer( + shape = RoundedCornerShape(4.dp), + modifier = Modifier.size(64.dp, 32.dp), + color = FluentTheme.colors.controlStrong.default, + border = BorderStroke(1.dp, FluentTheme.colors.controlStrong.default), + backgroundSizing = BackgroundSizing.InnerBorderEdge, + content = {} + ) Card(Modifier) { Box(Modifier.size(32.dp)) }