Use height-constrained CompactSearchInput inside SearchBarRow to prevent topbar height dynamic expansion

This commit is contained in:
Hy4ri
2026-06-19 19:17:55 +03:00
parent 11c5da260d
commit 3734cb361f
@@ -14,6 +14,7 @@ import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -35,6 +36,7 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Send import androidx.compose.material.icons.automirrored.filled.Send
@@ -699,6 +701,72 @@ private fun ClarifyDialog(
) )
} }
@Composable
private fun CompactSearchInput(
value: String,
onValueChange: (String) -> Unit,
modifier: Modifier = Modifier,
isError: Boolean = false,
) {
val textColor = MaterialTheme.colorScheme.onSurface
val errorColor = MaterialTheme.colorScheme.error
val outlineColor = if (isError) errorColor else MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
BasicTextField(
value = value,
onValueChange = onValueChange,
modifier =
modifier
.height(40.dp)
.background(
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
shape = RoundedCornerShape(20.dp),
).border(
width = 1.dp,
color = outlineColor,
shape = RoundedCornerShape(20.dp),
).padding(horizontal = 12.dp),
singleLine = true,
textStyle = MaterialTheme.typography.bodyMedium.copy(color = textColor),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
decorationBox = { innerTextField ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(18.dp),
)
Box(modifier = Modifier.weight(1f)) {
if (value.isEmpty()) {
Text(
text = "Search messages…",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
)
}
innerTextField()
}
if (value.isNotEmpty()) {
IconButton(
onClick = { onValueChange("") },
modifier = Modifier.size(20.dp),
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = "Clear",
modifier = Modifier.size(16.dp),
)
}
}
}
},
)
}
@Composable @Composable
private fun SearchBarRow( private fun SearchBarRow(
searchQuery: String, searchQuery: String,
@@ -712,24 +780,17 @@ private fun SearchBarRow(
val isError = searchQuery.isNotEmpty() && searchMatchCount == 0 val isError = searchQuery.isNotEmpty() && searchMatchCount == 0
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier =
Modifier
.fillMaxWidth()
.height(48.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
OutlinedTextField( CompactSearchInput(
value = searchQuery, value = searchQuery,
onValueChange = onQueryChange, onValueChange = onQueryChange,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
placeholder = { Text("Search messages…") },
singleLine = true,
isError = isError, isError = isError,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
trailingIcon = {
if (searchQuery.isNotEmpty()) {
IconButton(onClick = { onQueryChange("") }) {
Icon(Icons.Filled.Close, contentDescription = "Clear")
}
}
},
) )
if (searchQuery.isNotEmpty()) { if (searchQuery.isNotEmpty()) {
val countText = val countText =
@@ -752,20 +813,34 @@ private fun SearchBarRow(
modifier = Modifier.padding(horizontal = 4.dp), modifier = Modifier.padding(horizontal = 4.dp),
) )
} }
IconButton(onClick = onNavigateUp, enabled = searchMatchCount > 0) { IconButton(
onClick = onNavigateUp,
enabled = searchMatchCount > 0,
modifier = Modifier.size(36.dp),
) {
Icon( Icon(
Icons.Filled.KeyboardArrowUp, Icons.Filled.KeyboardArrowUp,
contentDescription = "Previous match", contentDescription = "Previous match",
modifier = Modifier.size(20.dp),
) )
} }
IconButton(onClick = onNavigateDown, enabled = searchMatchCount > 0) { IconButton(
onClick = onNavigateDown,
enabled = searchMatchCount > 0,
modifier = Modifier.size(36.dp),
) {
Icon( Icon(
Icons.Filled.KeyboardArrowDown, Icons.Filled.KeyboardArrowDown,
contentDescription = "Next match", contentDescription = "Next match",
modifier = Modifier.size(20.dp),
) )
} }
IconButton(onClick = onClose) { IconButton(onClick = onClose, modifier = Modifier.size(36.dp)) {
Icon(Icons.Filled.Close, contentDescription = "Close search") Icon(
Icons.Filled.Close,
contentDescription = "Close search",
modifier = Modifier.size(20.dp),
)
} }
} }
} }