Section facelift: LIGHT canvas swap, equal padding, 2dp item dividers

- LIGHT canvas (themedBackground) now paints the off-white formula
  (bg.mixWith(onBackground, 0.95f)) so white cards read as raised.
  DARK/BLACK keep palette bg (cards already raised via founder's
  formula in Section.kt). SIMPLEX keeps its gradient.
- Section cards in LIGHT switch from formula to pure white via
  Color.White. DARK/BLACK keep the formula, unchanged.
- Section card horizontal padding equalized to 16dp on outer + inner
  for clean canvas-edge alignment. extraPadding (icon-indented rows)
  keeps DEFAULT_PADDING * 1.7f.
- 2dp dividers between rows inside section cards, color matches the
  per-theme canvas (SIMPLEX uses gradient bottom stop). Implemented via
  Modifier.drawBehind on each SectionItemView, gated by a private
  LocalInSectionCard CompositionLocal set true only by SectionView's
  inner Column — standalone SectionItemView usage (alerts, pickers)
  stays unaffected. Single canvas helper canvasColorForCurrentTheme()
  in Theme.kt is the source of truth for both canvas paint and divider
  color.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
another-simple-pixel
2026-05-16 07:25:29 -07:00
parent 54f890ad88
commit aeb48d522e
2 changed files with 80 additions and 35 deletions
@@ -596,13 +596,30 @@ data class ThemeModeOverride (
}
}
// Canvas color for settings/info screens (drawn behind cards by themedBackground)
// and for the 2dp item divider inside section cards (matches canvas so dividers
// read as gaps showing the screen behind).
// LIGHT: formula derives off-white from palette bg + onBackground — lifts white
// cards above. DARK/BLACK: palette bg (cards already raised via founder's
// formula in Section.kt). SIMPLEX: gradient bottom stop (darker), since the
// canvas itself is a gradient drawn by themedBackgroundBrush.
fun canvasColorForCurrentTheme(): Color {
val theme = CurrentColors.value
val c = theme.colors
return when (theme.base) {
DefaultTheme.LIGHT -> c.background.mixWith(c.onBackground, 0.95f)
DefaultTheme.SIMPLEX -> c.background.darker(0.4f)
else -> c.background
}
}
fun Modifier.themedBackground(baseTheme: DefaultTheme = CurrentColors.value.base, bgLayerSize: MutableState<IntSize>?, bgLayer: GraphicsLayer?/*, shape: Shape = RectangleShape*/): Modifier {
return drawBehind {
copyBackgroundToAppBar(bgLayerSize, bgLayer) {
if (baseTheme == DefaultTheme.SIMPLEX) {
drawRect(brush = themedBackgroundBrush())
} else {
drawRect(CurrentColors.value.colors.background)
drawRect(canvasColorForCurrentTheme())
}
}
}
@@ -6,6 +6,8 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalDensity
@@ -23,10 +25,25 @@ import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
import chat.simplex.res.MR
private val SectionCardShape = RoundedCornerShape(16.dp)
private val CARD_PADDING = 16.dp
// Set to true by SectionView around its inner Column. SectionItemView reads it
// to decide whether to draw the 2dp bottom divider. False default keeps
// standalone usage (alerts, pickers, custom contexts) unaffected.
private val LocalInSectionCard = staticCompositionLocalOf { false }
@Composable
private fun Modifier.sectionItemDivider(): Modifier {
if (!LocalInSectionCard.current) return this
return this.drawBehind {
drawLine(canvasColorForCurrentTheme(), Offset(0f, size.height), Offset(size.width, size.height), strokeWidth = 2.dp.toPx())
}
}
@Composable
fun SectionView(title: String? = null, contentPadding: PaddingValues = PaddingValues(), headerBottomPadding: Dp = 8.dp, content: (@Composable ColumnScope.() -> Unit)) {
val cardColor = MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.95f)
val cardColor = if (CurrentColors.value.base == DefaultTheme.LIGHT) Color.White
else MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.95f)
Column {
if (title != null) {
Text(
@@ -34,14 +51,16 @@ fun SectionView(title: String? = null, contentPadding: PaddingValues = PaddingVa
modifier = Modifier.padding(start = DEFAULT_PADDING + DEFAULT_PADDING_HALF, bottom = headerBottomPadding), fontSize = 12.sp
)
}
Column(
Modifier
.padding(horizontal = DEFAULT_PADDING_HALF)
.fillMaxWidth()
.clip(SectionCardShape)
.background(cardColor)
.padding(contentPadding)
) { content() }
CompositionLocalProvider(LocalInSectionCard provides true) {
Column(
Modifier
.padding(horizontal = CARD_PADDING)
.fillMaxWidth()
.clip(SectionCardShape)
.background(cardColor)
.padding(contentPadding)
) { content() }
}
}
}
@@ -54,7 +73,8 @@ fun SectionView(
padding: PaddingValues = PaddingValues(),
content: (@Composable ColumnScope.() -> Unit)
) {
val cardColor = MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.95f)
val cardColor = if (CurrentColors.value.base == DefaultTheme.LIGHT) Color.White
else MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.95f)
Column {
val iconSize = with(LocalDensity.current) { 21.sp.toDp() }
Row(Modifier.padding(start = DEFAULT_PADDING + DEFAULT_PADDING_HALF, bottom = 5.dp), verticalAlignment = Alignment.CenterVertically) {
@@ -62,20 +82,23 @@ fun SectionView(
Text(title, color = MaterialTheme.colors.secondary, style = MaterialTheme.typography.body2, fontSize = 12.sp)
if (!leadingIcon) Icon(icon, null, Modifier.padding(start = DEFAULT_PADDING_HALF).size(iconSize), tint = iconTint)
}
Column(
Modifier
.padding(horizontal = DEFAULT_PADDING_HALF)
.fillMaxWidth()
.clip(SectionCardShape)
.background(cardColor)
.padding(padding)
) { content() }
CompositionLocalProvider(LocalInSectionCard provides true) {
Column(
Modifier
.padding(horizontal = CARD_PADDING)
.fillMaxWidth()
.clip(SectionCardShape)
.background(cardColor)
.padding(padding)
) { content() }
}
}
}
@Composable
fun SectionViewWithButton(title: String? = null, titleButton: (@Composable () -> Unit)?, contentPadding: PaddingValues = PaddingValues(), headerBottomPadding: Dp = 8.dp, content: (@Composable ColumnScope.() -> Unit)) {
val cardColor = MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.95f)
val cardColor = if (CurrentColors.value.base == DefaultTheme.LIGHT) Color.White
else MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.95f)
Column {
if (title != null || titleButton != null) {
Row(modifier = Modifier.padding(start = DEFAULT_PADDING + DEFAULT_PADDING_HALF, end = DEFAULT_PADDING + DEFAULT_PADDING_HALF, bottom = headerBottomPadding).fillMaxWidth()) {
@@ -88,14 +111,16 @@ fun SectionViewWithButton(title: String? = null, titleButton: (@Composable () ->
}
}
}
Column(
Modifier
.padding(horizontal = DEFAULT_PADDING_HALF)
.fillMaxWidth()
.clip(SectionCardShape)
.background(cardColor)
.padding(contentPadding)
) { content() }
CompositionLocalProvider(LocalInSectionCard provides true) {
Column(
Modifier
.padding(horizontal = CARD_PADDING)
.fillMaxWidth()
.clip(SectionCardShape)
.background(cardColor)
.padding(contentPadding)
) { content() }
}
}
}
@@ -149,14 +174,15 @@ fun SectionItemView(
disabled: Boolean = false,
extraPadding: Boolean = false,
padding: PaddingValues = if (extraPadding)
PaddingValues(start = DEFAULT_PADDING * 1.7f, end = DEFAULT_PADDING, top = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL, bottom = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL)
PaddingValues(start = DEFAULT_PADDING * 1.7f, end = CARD_PADDING, top = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL, bottom = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL)
else
PaddingValues(horizontal = DEFAULT_PADDING, vertical = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL),
PaddingValues(horizontal = CARD_PADDING, vertical = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL),
content: (@Composable RowScope.() -> Unit)
) {
val modifier = Modifier
.fillMaxWidth()
.sizeIn(minHeight = minHeight)
.sectionItemDivider()
Row(
if (click == null || disabled) modifier.padding(padding) else modifier.clickable(onClick = click).padding(padding),
verticalAlignment = Alignment.CenterVertically
@@ -172,9 +198,9 @@ fun SectionItemViewWithoutMinPadding(
disabled: Boolean = false,
extraPadding: Boolean = false,
padding: PaddingValues = if (extraPadding)
PaddingValues(start = DEFAULT_PADDING * 1.7f, end = DEFAULT_PADDING)
PaddingValues(start = DEFAULT_PADDING * 1.7f, end = CARD_PADDING)
else
PaddingValues(horizontal = DEFAULT_PADDING),
PaddingValues(horizontal = CARD_PADDING),
content: (@Composable RowScope.() -> Unit)
) {
SectionItemView(click, minHeight, disabled, extraPadding, padding, content)
@@ -188,14 +214,15 @@ fun SectionItemViewLongClickable(
disabled: Boolean = false,
extraPadding: Boolean = false,
padding: PaddingValues = if (extraPadding)
PaddingValues(start = DEFAULT_PADDING * 1.7f, end = DEFAULT_PADDING, top = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL, bottom = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL)
PaddingValues(start = DEFAULT_PADDING * 1.7f, end = CARD_PADDING, top = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL, bottom = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL)
else
PaddingValues(horizontal = DEFAULT_PADDING, vertical = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL),
PaddingValues(horizontal = CARD_PADDING, vertical = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL),
content: (@Composable RowScope.() -> Unit)
) {
val modifier = Modifier
.fillMaxWidth()
.sizeIn(minHeight = minHeight)
.sectionItemDivider()
Row(
if (disabled) {
modifier.padding(padding)
@@ -213,13 +240,14 @@ fun SectionItemViewSpaceBetween(
click: (() -> Unit)? = null,
onLongClick: (() -> Unit)? = null,
minHeight: Dp = DEFAULT_MIN_SECTION_ITEM_HEIGHT,
padding: PaddingValues = PaddingValues(horizontal = DEFAULT_PADDING),
padding: PaddingValues = PaddingValues(horizontal = CARD_PADDING),
disabled: Boolean = false,
content: (@Composable RowScope.() -> Unit)
) {
val modifier = Modifier
.fillMaxWidth()
.sizeIn(minHeight = minHeight)
.sectionItemDivider()
Row(
if (click == null || disabled) modifier.padding(padding).padding(vertical = DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL) else modifier
.combinedClickable(onClick = click, onLongClick = onLongClick).padding(padding)