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
|
if (!isStreaming) {
|
||||||
val showScrollUp by remember { derivedStateOf { listState.firstVisibleItemIndex > 2 } }
|
val atTop by remember { derivedStateOf { listState.firstVisibleItemIndex <= 1 } }
|
||||||
val showScrollDown by remember { derivedStateOf {
|
val atBottom by remember { derivedStateOf {
|
||||||
val info = listState.layoutInfo
|
val info = listState.layoutInfo
|
||||||
info.visibleItemsInfo.lastOrNull()?.index != info.totalItemsCount - 1
|
info.visibleItemsInfo.lastOrNull()?.index == info.totalItemsCount - 1
|
||||||
}}
|
}}
|
||||||
|
val canScrollUp by remember { derivedStateOf { listState.canScrollBackward } }
|
||||||
|
val canScrollDown by remember { derivedStateOf { listState.canScrollForward } }
|
||||||
|
|
||||||
if (showScrollUp || showScrollDown) {
|
if (!atTop && canScrollUp) {
|
||||||
Column(
|
GlassSurface(
|
||||||
modifier = Modifier
|
shape = KaizenShapes.circle,
|
||||||
.align(Alignment.CenterEnd)
|
tintAlpha = GlassTiers.pill(isSystemInDarkTheme()),
|
||||||
.padding(end = 8.dp),
|
shadowStack = KaizenShadows.level1,
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
cornerRadius = 18.dp,
|
||||||
) {
|
modifier = Modifier
|
||||||
if (showScrollUp) {
|
.align(Alignment.CenterEnd)
|
||||||
GlassSurface(
|
.padding(end = 8.dp, bottom = 24.dp)
|
||||||
shape = KaizenShapes.circle,
|
.size(36.dp)
|
||||||
tintAlpha = GlassTiers.pill(isSystemInDarkTheme()),
|
.clickable { scope.launch { listState.animateScrollToItem(0) } },
|
||||||
shadowStack = KaizenShadows.level1,
|
) {
|
||||||
cornerRadius = 18.dp,
|
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
modifier = Modifier
|
Icon(Icons.Rounded.KeyboardArrowUp, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.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) {
|
} else if (!atBottom && canScrollDown) {
|
||||||
GlassSurface(
|
GlassSurface(
|
||||||
shape = KaizenShapes.circle,
|
shape = KaizenShapes.circle,
|
||||||
tintAlpha = GlassTiers.pill(isSystemInDarkTheme()),
|
tintAlpha = GlassTiers.pill(isSystemInDarkTheme()),
|
||||||
shadowStack = KaizenShadows.level1,
|
shadowStack = KaizenShadows.level1,
|
||||||
cornerRadius = 18.dp,
|
cornerRadius = 18.dp,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(36.dp)
|
.align(Alignment.CenterEnd)
|
||||||
.clickable { scope.launch { listState.animateScrollToItem(messages.size - 1) } },
|
.padding(end = 8.dp, top = 24.dp)
|
||||||
) {
|
.size(36.dp)
|
||||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
.clickable { scope.launch { listState.animateScrollToItem(messages.size - 1) } },
|
||||||
Icon(Icons.Rounded.KeyboardArrowDown, null, tint = MaterialTheme.colorScheme.onBackground, modifier = Modifier.size(20.dp))
|
) {
|
||||||
}
|
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.Settings
|
||||||
import androidx.compose.material.icons.rounded.Star
|
import androidx.compose.material.icons.rounded.Star
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -423,48 +422,38 @@ private fun HistoryItemRow(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DropdownMenu(
|
androidx.compose.material3.DropdownMenu(
|
||||||
expanded = showMenu,
|
expanded = showMenu,
|
||||||
onDismissRequest = { showMenu = false },
|
onDismissRequest = { showMenu = false },
|
||||||
|
containerColor = if (isDark) Color(0xFF1A2030) else Color(0xFFF8F9FB),
|
||||||
|
shadowElevation = 12.dp,
|
||||||
|
shape = KaizenShapes.md,
|
||||||
) {
|
) {
|
||||||
if (!item.locked) {
|
if (!item.locked) {
|
||||||
DropdownMenuItem(
|
MenuRow(Icons.Rounded.Edit, stringResource(R.string.sidebar_rename), cs.onBackground) { showMenu = false; onRename() }
|
||||||
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)) },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
DropdownMenuItem(
|
MenuRow(Icons.Rounded.PushPin, stringResource(if (item.pinned) R.string.sidebar_unpin else R.string.sidebar_pin), cs.onBackground) { showMenu = false; onTogglePin() }
|
||||||
text = {
|
MenuRow(
|
||||||
Text(
|
if (item.locked) Icons.Rounded.LockOpen else Icons.Rounded.Lock,
|
||||||
stringResource(if (item.pinned) R.string.sidebar_unpin else R.string.sidebar_pin),
|
stringResource(if (item.locked) R.string.sidebar_unlock else R.string.sidebar_lock),
|
||||||
fontSize = 14.sp,
|
cs.onBackground,
|
||||||
)
|
) { showMenu = false; onToggleLock() }
|
||||||
},
|
MenuRow(Icons.Rounded.Delete, stringResource(R.string.sidebar_delete), Color(0xFFEF4444)) { showMenu = false; onDelete() }
|
||||||
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)) },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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