Improve focus rendering: now display a blue inset ring around the focused component.
Closes #6397
This commit is contained in:
@@ -16,7 +16,11 @@ import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalRippleConfiguration
|
||||
import androidx.compose.material3.LocalRippleThemeConfiguration
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RippleConfiguration
|
||||
import androidx.compose.material3.RippleThemeConfiguration
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
@@ -28,6 +32,7 @@ import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.tokens.compoundTypography
|
||||
import io.element.android.compound.tokens.generated.SemanticColors
|
||||
import io.element.android.compound.tokens.generated.TypographyTokens
|
||||
@@ -159,6 +164,25 @@ fun ElementTheme(
|
||||
CompositionLocalProvider(
|
||||
LocalCompoundColors provides currentCompoundColor,
|
||||
LocalContentColor provides colorScheme.onSurface,
|
||||
// Configure the keyboard focus style: Draw a blue inset ring around the focused component.
|
||||
// By default, a semi-transparent black overlay is used
|
||||
LocalRippleThemeConfiguration provides RippleThemeConfiguration(
|
||||
RippleThemeConfiguration.Focus.InsetRing(
|
||||
outerStrokeInset = 0.dp,
|
||||
outerStrokeWidth = 2.dp,
|
||||
innerStrokeInset = 0.dp,
|
||||
innerStrokeWidth = 0.dp,
|
||||
)
|
||||
),
|
||||
// Configure the keyboard focus color.
|
||||
// By default, ColorScheme.secondaryColor is used
|
||||
LocalRippleConfiguration provides
|
||||
RippleConfiguration(
|
||||
focus = RippleConfiguration.Focus.InsetRing(
|
||||
outerStrokeColor = ElementTheme.colors.borderFocused,
|
||||
innerStrokeColor = Color.Transparent,
|
||||
)
|
||||
),
|
||||
) {
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
|
||||
+62
-45
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user