feat(ui): implement tablet optimizations and gemini-style dynamic keyboard suggestion-hiding
This commit is contained in:
parent
e13f6798db
commit
e3ea6d241f
2 changed files with 34 additions and 12 deletions
|
|
@ -21,10 +21,14 @@ import androidx.compose.foundation.layout.Column
|
|||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.ime
|
||||
import androidx.compose.foundation.layout.isImeVisible
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
|
|
@ -129,6 +133,11 @@ fun EmptyHero(userName: String, onPick: (String) -> Unit, modifier: Modifier = M
|
|||
now.format(DateTimeFormatter.ofPattern("EEE, d. MMMM · HH:mm", Locale.GERMAN))
|
||||
}
|
||||
|
||||
// Dynamic keyboard visibility check (Google Gemini style)
|
||||
// When the soft keyboard is visible, we completely hide the Suggestion Pills
|
||||
// to give the user maximum typing space and keep the layout extremely clean.
|
||||
val isKeyboardOpen = WindowInsets.isImeVisible
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
|
|
@ -144,8 +153,12 @@ fun EmptyHero(userName: String, onPick: (String) -> Unit, modifier: Modifier = M
|
|||
Text("Was liegt an?", color = cs.onBackground, fontSize = 32.sp, fontWeight = FontWeight.Bold)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(dateLine, color = cs.onSurfaceVariant.copy(alpha = 0.7f), fontSize = 13.sp)
|
||||
Spacer(Modifier.height(32.dp))
|
||||
SuggestionPills(onPick)
|
||||
|
||||
if (!isKeyboardOpen) {
|
||||
Spacer(Modifier.height(32.dp))
|
||||
SuggestionPills(onPick)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(142.dp)) // Clear the floating bottom dock
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
|
|
@ -23,6 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.kaizen.app.haptics.rememberHaptics
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -39,6 +41,10 @@ fun ChatScreen(userName: String = "Bruno") {
|
|||
var nextId by remember { mutableStateOf(0L) }
|
||||
var selectedMode by remember { mutableStateOf("Standard") }
|
||||
|
||||
// Tablet & Landscape optimization: Detect large screens and cap the content width
|
||||
val configuration = LocalConfiguration.current
|
||||
val isTablet = configuration.screenWidthDp > 600
|
||||
|
||||
fun send(text: String) {
|
||||
val trimmed = text.trim()
|
||||
if (trimmed.isEmpty() || isStreaming) return
|
||||
|
|
@ -78,21 +84,24 @@ fun ChatScreen(userName: String = "Bruno") {
|
|||
}
|
||||
|
||||
MeshBackground {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
// 1. Scrollable Chat Content
|
||||
// Stretches to the full screen, allowing bubbles to scroll cleanly behind the floating glass panels.
|
||||
// Since the OS physically resizes the window when the keyboard opens, the Box container shrinks natively,
|
||||
// placing the bottom dock exactly on top of the keyboard. We use constant Paddings to clear the docks.
|
||||
// Responsive width capping and centering on tablets.
|
||||
if (messages.isEmpty()) {
|
||||
EmptyHero(
|
||||
userName = userName,
|
||||
onPick = { haptics.tick(); send(it) },
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier)
|
||||
)
|
||||
} else {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier),
|
||||
contentPadding = PaddingValues(
|
||||
top = 92.dp, // Leaves luxurious room for the floating KaizenHeader + status bars
|
||||
bottom = 152.dp, // Leaves luxurious room for the floating ModePills + ChatInput + bottom margins
|
||||
|
|
@ -105,20 +114,20 @@ fun ChatScreen(userName: String = "Bruno") {
|
|||
}
|
||||
}
|
||||
|
||||
// 2. Floating Header (Translucent Frosted Glass)
|
||||
// 2. Floating Header (Translucent Frosted Glass, responsive centering)
|
||||
KaizenHeader(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopCenter)
|
||||
.statusBarsPadding()
|
||||
.then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier)
|
||||
)
|
||||
|
||||
// 3. Floating Bottom Control Dock
|
||||
// Positioned at the bottom of the window. When the keyboard is active, the OS resizes the window
|
||||
// and this dock naturally sits exactly on top of the keyboard without any double-padding.
|
||||
// 3. Floating Bottom Control Dock (responsive centering and clear bottom margins)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.navigationBarsPadding()
|
||||
.then(if (isTablet) Modifier.widthIn(max = 720.dp) else Modifier)
|
||||
) {
|
||||
ModePillsRow(selected = selectedMode, onSelect = { selectedMode = it; haptics.tick() })
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
|
@ -128,7 +137,7 @@ fun ChatScreen(userName: String = "Bruno") {
|
|||
onSend = { send(input) },
|
||||
enabled = !isStreaming,
|
||||
)
|
||||
Spacer(Modifier.height(12.dp)) // Extra breathing margin at the bottom of the floating card
|
||||
Spacer(Modifier.height(16.dp)) // Increased from 12.dp to 16.dp to completely clear keyboard edges on all screens!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue