fix scroll buttons + restyle sidebar context menu
Scroll buttons: only one shown at a time based on direction (up when scrolled down, down when scrolled up), hidden during streaming. Mutually exclusive instead of both visible. Sidebar menu: replaced default Material3 DropdownMenu/MenuItem with custom styled menu — dark surface color (1A2030 dark, F8F9FB light), KaizenShapes.md rounded corners, 12dp shadow elevation, custom MenuRow composable with proper spacing.
This commit is contained in:
parent
f62cc4b7ac
commit
fb269731b7
2 changed files with 64 additions and 78 deletions
|
|
@ -569,48 +569,45 @@ fun ChatScreen(
|
|||
}
|
||||
}
|
||||
|
||||
// Scroll buttons
|
||||
val showScrollUp by remember { derivedStateOf { listState.firstVisibleItemIndex > 2 } }
|
||||
val showScrollDown by remember { derivedStateOf {
|
||||
val info = listState.layoutInfo
|
||||
info.visibleItemsInfo.lastOrNull()?.index != info.totalItemsCount - 1
|
||||
}}
|
||||
if (!isStreaming) {
|
||||
val atTop by remember { derivedStateOf { listState.firstVisibleItemIndex <= 1 } }
|
||||
val atBottom by remember { derivedStateOf {
|
||||
val info = listState.layoutInfo
|
||||
info.visibleItemsInfo.lastOrNull()?.index == info.totalItemsCount - 1
|
||||
}}
|
||||
val canScrollUp by remember { derivedStateOf { listState.canScrollBackward } }
|
||||
val canScrollDown by remember { derivedStateOf { listState.canScrollForward } }
|
||||
|
||||
if (showScrollUp || showScrollDown) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.padding(end = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
if (showScrollUp) {
|
||||
GlassSurface(
|
||||
shape = KaizenShapes.circle,
|
||||
tintAlpha = GlassTiers.pill(isSystemInDarkTheme()),
|
||||
shadowStack = KaizenShadows.level1,
|
||||
cornerRadius = 18.dp,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clickable { scope.launch { listState.animateScrollToItem(0) } },
|
||||
) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Icon(Icons.Rounded.KeyboardArrowUp, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp))
|
||||
}
|
||||
if (!atTop && canScrollUp) {
|
||||
GlassSurface(
|
||||
shape = KaizenShapes.circle,
|
||||
tintAlpha = GlassTiers.pill(isSystemInDarkTheme()),
|
||||
shadowStack = KaizenShadows.level1,
|
||||
cornerRadius = 18.dp,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.padding(end = 8.dp, bottom = 24.dp)
|
||||
.size(36.dp)
|
||||
.clickable { scope.launch { listState.animateScrollToItem(0) } },
|
||||
) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Icon(Icons.Rounded.KeyboardArrowUp, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp))
|
||||
}
|
||||
}
|
||||
if (showScrollDown) {
|
||||
GlassSurface(
|
||||
shape = KaizenShapes.circle,
|
||||
tintAlpha = GlassTiers.pill(isSystemInDarkTheme()),
|
||||
shadowStack = KaizenShadows.level1,
|
||||
cornerRadius = 18.dp,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clickable { scope.launch { listState.animateScrollToItem(messages.size - 1) } },
|
||||
) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Icon(Icons.Rounded.KeyboardArrowDown, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp))
|
||||
}
|
||||
} else if (!atBottom && canScrollDown) {
|
||||
GlassSurface(
|
||||
shape = KaizenShapes.circle,
|
||||
tintAlpha = GlassTiers.pill(isSystemInDarkTheme()),
|
||||
shadowStack = KaizenShadows.level1,
|
||||
cornerRadius = 18.dp,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.padding(end = 8.dp, top = 24.dp)
|
||||
.size(36.dp)
|
||||
.clickable { scope.launch { listState.animateScrollToItem(messages.size - 1) } },
|
||||
) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Icon(Icons.Rounded.KeyboardArrowDown, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ import androidx.compose.material.icons.rounded.Search
|
|||
import androidx.compose.material.icons.rounded.Settings
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -423,48 +422,38 @@ private fun HistoryItemRow(
|
|||
}
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
androidx.compose.material3.DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false },
|
||||
containerColor = if (isDark) Color(0xFF1A2030) else Color(0xFFF8F9FB),
|
||||
shadowElevation = 12.dp,
|
||||
shape = KaizenShapes.md,
|
||||
) {
|
||||
if (!item.locked) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.sidebar_rename), fontSize = 14.sp) },
|
||||
onClick = { showMenu = false; onRename() },
|
||||
leadingIcon = { Icon(Icons.Rounded.Edit, null, modifier = Modifier.size(18.dp)) },
|
||||
)
|
||||
MenuRow(Icons.Rounded.Edit, stringResource(R.string.sidebar_rename), cs.onBackground) { showMenu = false; onRename() }
|
||||
}
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
stringResource(if (item.pinned) R.string.sidebar_unpin else R.string.sidebar_pin),
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
},
|
||||
onClick = { showMenu = false; onTogglePin() },
|
||||
leadingIcon = { Icon(Icons.Rounded.PushPin, null, modifier = Modifier.size(18.dp)) },
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(
|
||||
stringResource(if (item.locked) R.string.sidebar_unlock else R.string.sidebar_lock),
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
},
|
||||
onClick = { showMenu = false; onToggleLock() },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
if (item.locked) Icons.Rounded.LockOpen else Icons.Rounded.Lock,
|
||||
null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.sidebar_delete), fontSize = 14.sp, color = Color(0xFFEF4444)) },
|
||||
onClick = { showMenu = false; onDelete() },
|
||||
leadingIcon = { Icon(Icons.Rounded.Delete, null, tint = Color(0xFFEF4444), modifier = Modifier.size(18.dp)) },
|
||||
)
|
||||
MenuRow(Icons.Rounded.PushPin, stringResource(if (item.pinned) R.string.sidebar_unpin else R.string.sidebar_pin), cs.onBackground) { showMenu = false; onTogglePin() }
|
||||
MenuRow(
|
||||
if (item.locked) Icons.Rounded.LockOpen else Icons.Rounded.Lock,
|
||||
stringResource(if (item.locked) R.string.sidebar_unlock else R.string.sidebar_lock),
|
||||
cs.onBackground,
|
||||
) { showMenu = false; onToggleLock() }
|
||||
MenuRow(Icons.Rounded.Delete, stringResource(R.string.sidebar_delete), Color(0xFFEF4444)) { showMenu = false; onDelete() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MenuRow(icon: ImageVector, label: String, tint: Color, onClick: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(icon, null, tint = tint.copy(alpha = 0.7f), modifier = Modifier.size(18.dp))
|
||||
Spacer(Modifier.width(12.dp))
|
||||
Text(label, color = tint, fontSize = 14.sp, fontWeight = FontWeight.Medium)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue