Merge branch 'develop' into feature/valere/timeline_active_call_rendering

This commit is contained in:
Valere
2026-07-16 10:51:34 +02:00
266 changed files with 3081 additions and 923 deletions
@@ -29,21 +29,32 @@ fun EqualWidthColumn(
) {
SubcomposeLayout(modifier = modifier) { constraints ->
val measurables = subcompose(0, content).map { it.measure(constraints) }
val maxWidth = measurables.maxOf { it.width }
val newConstraints = constraints.copy(minWidth = maxWidth)
val newMeasurables = if (measurables.all { it.width == maxWidth }) {
// Skip re-measuring if all children have the same width
measurables
if (measurables.isEmpty()) {
// No children, so just return a layout with 0 width and height
layout(0, 0) {}
} else if (measurables.size == 1) {
// No need to re-measure if there's only one child
val measurable = measurables.first()
layout(measurable.width, measurable.height) {
measurable.placeRelative(0, 0)
}
} else {
// Re-measure with the largest width as the minWidth to have all children constrained to the same width
subcompose(1, content).map { it.measure(newConstraints) }
}
val totalHeight = (newMeasurables.sumOf { it.height } + spacing.toPx() * (newMeasurables.size - 1)).roundToInt()
layout(maxWidth, totalHeight) {
var yPosition = 0
newMeasurables.forEach { measurable ->
measurable.placeRelative(0, yPosition)
yPosition += measurable.height + spacing.roundToPx()
val maxWidth = measurables.maxOf { it.width }
val newConstraints = constraints.copy(minWidth = maxWidth)
val newMeasurables = if (measurables.all { it.width == maxWidth }) {
// Skip re-measuring if all children have the same width
measurables
} else {
// Re-measure with the largest width as the minWidth to have all children constrained to the same width
subcompose(1, content).map { it.measure(newConstraints) }
}
val totalHeight = (newMeasurables.sumOf { it.height } + spacing.toPx() * (newMeasurables.size - 1)).roundToInt()
layout(maxWidth, totalHeight) {
var yPosition = 0
newMeasurables.forEach { measurable ->
measurable.placeRelative(0, yPosition)
yPosition += measurable.height + spacing.roundToPx()
}
}
}
}
@@ -28,13 +28,15 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalRippleThemeConfiguration
import androidx.compose.material3.RippleThemeConfiguration
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
@@ -181,11 +183,8 @@ private fun ButtonInternal(
ButtonSize.LargeLowPadding -> PaddingValues(horizontal = lowHorizontalPaddingValue, vertical = 13.dp)
}
val shape = when (style) {
ButtonStyle.Filled,
ButtonStyle.Outlined -> RoundedCornerShape(percent = 50)
ButtonStyle.Text -> RectangleShape
}
// Apply the same shape to all buttons, so that the focus ring is consistent across styles.
val shape = RoundedCornerShape(percent = 50)
val border = when (style) {
ButtonStyle.Filled -> null
@@ -202,48 +201,66 @@ private fun ButtonInternal(
ButtonStyle.Text -> null
}
androidx.compose.material3.Button(
onClick = {
if (!showProgress) {
onClick()
}
},
modifier = modifier.heightIn(min = minHeight),
enabled = enabled,
shape = shape,
colors = colors,
elevation = null,
border = border,
contentPadding = contentPadding,
interactionSource = remember { MutableInteractionSource() },
// Reduce outerStrokeInset on Filled buttons so that it's displayed around the button
val outerStrokeInset = when (style) {
ButtonStyle.Filled -> (-4).dp
ButtonStyle.Outlined,
ButtonStyle.Text -> 0.dp
}
CompositionLocalProvider(
LocalRippleThemeConfiguration provides RippleThemeConfiguration(
RippleThemeConfiguration.Focus.InsetRing(
outerStrokeInset = outerStrokeInset,
outerStrokeWidth = 2.dp,
innerStrokeInset = 0.dp,
innerStrokeWidth = 0.dp,
)
),
) {
when {
showProgress -> {
CircularProgressIndicator(
modifier = Modifier
.progressSemantics()
.size(20.dp),
color = LocalContentColor.current,
strokeWidth = 2.dp,
)
Spacer(modifier = Modifier.width(8.dp))
}
leadingIcon != null -> {
Icon(
painter = leadingIcon.getPainter(),
contentDescription = null,
tint = LocalContentColor.current,
modifier = Modifier.size(20.dp),
)
Spacer(modifier = Modifier.width(8.dp))
androidx.compose.material3.Button(
onClick = {
if (!showProgress) {
onClick()
}
},
modifier = modifier.heightIn(min = minHeight),
enabled = enabled,
shape = shape,
colors = colors,
elevation = null,
border = border,
contentPadding = contentPadding,
interactionSource = remember { MutableInteractionSource() },
) {
when {
showProgress -> {
CircularProgressIndicator(
modifier = Modifier
.progressSemantics()
.size(20.dp),
color = LocalContentColor.current,
strokeWidth = 2.dp,
)
Spacer(modifier = Modifier.width(8.dp))
}
leadingIcon != null -> {
Icon(
painter = leadingIcon.getPainter(),
contentDescription = null,
tint = LocalContentColor.current,
modifier = Modifier.size(20.dp),
)
Spacer(modifier = Modifier.width(8.dp))
}
}
Text(
text = text,
style = ElementTheme.typography.fontBodyLgMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
Text(
text = text,
style = ElementTheme.typography.fontBodyLgMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
@@ -294,7 +311,7 @@ internal enum class ButtonStyle {
@Composable
fun getColors(destructive: Boolean): ButtonColors = when (this) {
Filled -> ButtonDefaults.buttonColors(
Filled -> ButtonDefaults.filledTonalButtonColors(
containerColor = getPrimaryColor(destructive),
contentColor = ElementTheme.materialColors.onPrimary,
disabledContainerColor = if (destructive) {
@@ -304,13 +321,13 @@ internal enum class ButtonStyle {
},
disabledContentColor = ElementTheme.colors.textOnSolidPrimary
)
Outlined -> ButtonDefaults.buttonColors(
Outlined -> ButtonDefaults.outlinedButtonColors(
containerColor = Color.Transparent,
contentColor = getPrimaryColor(destructive),
disabledContainerColor = Color.Transparent,
disabledContentColor = getDisabledContentColor(destructive),
)
Text -> ButtonDefaults.buttonColors(
Text -> ButtonDefaults.textButtonColors(
containerColor = Color.Transparent,
contentColor = if (destructive) {
ElementTheme.colors.textCriticalPrimary
@@ -139,7 +139,7 @@ private fun SearchFieldContainer(
border = BorderStroke(
width = 1.dp,
color = if (isFocused) {
ElementTheme.colors.borderInteractiveHovered
ElementTheme.colors.borderFocused
} else {
ElementTheme.colors.borderInteractiveSecondary
}
@@ -293,8 +293,8 @@ private fun TextFieldContainer(
width = if (isFocused) 2.dp else 1.dp,
color = when {
!enabled -> ElementTheme.colors.borderDisabled
isFocused -> ElementTheme.colors.borderFocused
isError -> ElementTheme.colors.borderCriticalPrimary
isFocused -> ElementTheme.colors.borderInteractiveHovered
else -> ElementTheme.colors.borderInteractiveSecondary
}
)