mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-22 15:14:40 -04:00
Move build status to launcher rail footer
This commit is contained in:
1 parent
0cdb945d9c
commit
3e864fda17
1 file changed
+87
-86
@@ -54,12 +54,12 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -101,6 +101,9 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
@@ -291,82 +294,89 @@ private fun Rail(
|
||||
) {
|
||||
val colors = MaterialTheme.colorScheme
|
||||
Column(
|
||||
modifier = modifier.background(colors.surface).verticalScroll(rememberScrollState()).padding(horizontal = 10.dp, vertical = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = modifier.background(colors.surface).padding(horizontal = 10.dp, vertical = 12.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 8.dp, bottom = 10.dp)) {
|
||||
Image(painterResource(R.drawable.logo), null, modifier = Modifier.size(30.dp))
|
||||
Spacer(Modifier.width(10.dp))
|
||||
Column {
|
||||
Text("DroidDeck", fontSize = 18.sp, fontWeight = FontWeight.Bold, color = colors.onBackground)
|
||||
val status = when {
|
||||
s.busy -> if (s.percent >= 0) "${s.stage} ${s.percent}%" else s.stage
|
||||
!s.ready -> "runtime not installed"
|
||||
s.available != null && s.available != s.installed -> "runtime update available"
|
||||
else -> null
|
||||
Column(
|
||||
modifier = Modifier.weight(1f).fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 8.dp, bottom = 10.dp)) {
|
||||
Image(painterResource(R.drawable.logo), null, modifier = Modifier.size(30.dp))
|
||||
Spacer(Modifier.width(10.dp))
|
||||
Column {
|
||||
Text("DroidDeck", fontSize = 18.sp, fontWeight = FontWeight.Bold, color = colors.onBackground)
|
||||
val status = when {
|
||||
s.busy -> if (s.percent >= 0) "${s.stage} ${s.percent}%" else s.stage
|
||||
!s.ready -> "runtime not installed"
|
||||
s.available != null && s.available != s.installed -> "runtime update available"
|
||||
else -> null
|
||||
}
|
||||
if (status != null) Text(status, fontSize = 11.sp, color = colors.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
if (status != null) Text(status, fontSize = 11.sp, color = colors.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(s.busy, enter = expandVertically() + fadeIn(), exit = shrinkVertically() + fadeOut()) {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp))
|
||||
}
|
||||
AnimatedVisibility(s.busy, enter = expandVertically() + fadeIn(), exit = shrinkVertically() + fadeOut()) {
|
||||
LinearProgressIndicator(modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp))
|
||||
}
|
||||
|
||||
var lastRunning by remember { mutableStateOf("") }
|
||||
if (s.running != null) lastRunning = s.running
|
||||
AnimatedVisibility(
|
||||
s.running != null,
|
||||
enter = expandVertically(Motion.sp(0.75f)) + fadeIn(Motion.tw(300)) + slideInVertically(Motion.sp(0.6f)) { -it / 2 },
|
||||
exit = shrinkVertically(Motion.tw(220)) + fadeOut(Motion.tw(180)),
|
||||
) { RunningTile(lastRunning, a.onResume) }
|
||||
var lastRunning by remember { mutableStateOf("") }
|
||||
if (s.running != null) lastRunning = s.running
|
||||
AnimatedVisibility(
|
||||
s.running != null,
|
||||
enter = expandVertically(Motion.sp(0.75f)) + fadeIn(Motion.tw(300)) + slideInVertically(Motion.sp(0.6f)) { -it / 2 },
|
||||
exit = shrinkVertically(Motion.tw(220)) + fadeOut(Motion.tw(180)),
|
||||
) { RunningTile(lastRunning, a.onResume) }
|
||||
|
||||
val pal = LocalPalette.current
|
||||
var navOrigin by remember { mutableStateOf(Offset.Zero) }
|
||||
val positions = remember { mutableStateMapOf<String, Rect>() }
|
||||
Box(modifier = Modifier.fillMaxWidth().onGloballyPositioned { val o = it.positionInRoot(); if (o != navOrigin) navOrigin = o }) {
|
||||
val target = positions[selected]?.translate(-navOrigin)
|
||||
val y by animateFloatAsState(target?.top ?: 0f, Motion.sp(0.72f, Spring.StiffnessLow), label = "indY")
|
||||
val x by animateFloatAsState(target?.left ?: 0f, Motion.sp(0.9f), label = "indX")
|
||||
val h by animateFloatAsState(target?.height ?: 0f, Motion.sp(1f, Spring.StiffnessMediumLow), label = "indH")
|
||||
val w by animateFloatAsState(target?.width ?: 0f, Motion.sp(1f, Spring.StiffnessMediumLow), label = "indW")
|
||||
val alpha by animateFloatAsState(if (target != null) 1f else 0f, Motion.tw(250), label = "indA")
|
||||
val density = LocalDensity.current
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.offset { IntOffset(x.roundToInt(), y.roundToInt()) }
|
||||
.width(with(density) { w.toDp() }).height(with(density) { h.toDp() })
|
||||
.alpha(alpha)
|
||||
.graphicsLayer { shadowElevation = 6.dp.toPx(); shape = Shape10; clip = false; ambientShadowColor = pal.signal; spotShadowColor = pal.signal }
|
||||
.clip(Shape10)
|
||||
.background(Brush.linearGradient(listOf(colors.primary, pal.primary2))),
|
||||
val pal = LocalPalette.current
|
||||
var navOrigin by remember { mutableStateOf(Offset.Zero) }
|
||||
val positions = remember { mutableStateMapOf<String, Rect>() }
|
||||
Box(modifier = Modifier.fillMaxWidth().onGloballyPositioned { val o = it.positionInRoot(); if (o != navOrigin) navOrigin = o }) {
|
||||
val target = positions[selected]?.translate(-navOrigin)
|
||||
val y by animateFloatAsState(target?.top ?: 0f, Motion.sp(0.72f, Spring.StiffnessLow), label = "indY")
|
||||
val x by animateFloatAsState(target?.left ?: 0f, Motion.sp(0.9f), label = "indX")
|
||||
val h by animateFloatAsState(target?.height ?: 0f, Motion.sp(1f, Spring.StiffnessMediumLow), label = "indH")
|
||||
val w by animateFloatAsState(target?.width ?: 0f, Motion.sp(1f, Spring.StiffnessMediumLow), label = "indW")
|
||||
val alpha by animateFloatAsState(if (target != null) 1f else 0f, Motion.tw(250), label = "indA")
|
||||
val density = LocalDensity.current
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.offset { IntOffset(x.roundToInt(), y.roundToInt()) }
|
||||
.width(with(density) { w.toDp() }).height(with(density) { h.toDp() })
|
||||
.alpha(alpha)
|
||||
.graphicsLayer { shadowElevation = 6.dp.toPx(); shape = Shape10; clip = false; ambientShadowColor = pal.signal; spotShadowColor = pal.signal }
|
||||
.clip(Shape10)
|
||||
.background(Brush.linearGradient(listOf(colors.primary, pal.primary2))),
|
||||
)
|
||||
val register: (String, LayoutCoordinates) -> Unit = { key, c ->
|
||||
val o = c.positionInRoot(); val r = Rect(o.x, o.y, o.x + c.size.width, o.y + c.size.height)
|
||||
if (positions[key] != r) positions[key] = r
|
||||
}
|
||||
val unregister: (String) -> Unit = { positions.remove(it) }
|
||||
Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
if (s.isHomeApp) {
|
||||
NavItem("Android apps", "android-apps", selected == "android-apps", count = s.androidApps.size,
|
||||
register = register, unregister = unregister) { onSelect("android-apps") }
|
||||
}
|
||||
NavItem("Steam", "steam", selected == "steam", count = s.steamGames.size, register = register, unregister = unregister) { onSelect("steam") }
|
||||
NavItem("Desktop", "desktop", selected == "desktop", count = s.emulators.count { it.installed }, register = register, unregister = unregister) { onSelect("desktop") }
|
||||
NavItem("Setup", "setup", selected == "setup", register = register, unregister = unregister) { onSelect("setup") }
|
||||
Spacer(Modifier.height(6.dp))
|
||||
}
|
||||
}
|
||||
|
||||
val creditsSrc = remember { MutableInteractionSource() }
|
||||
val creditsHot = rememberHot(creditsSrc)
|
||||
Text(
|
||||
"credits", fontSize = 11.sp, color = if (creditsHot) LocalPalette.current.signal else colors.onSurfaceVariant,
|
||||
modifier = Modifier.clip(Shape10)
|
||||
.border(2.dp, if (creditsHot) LocalPalette.current.signal else Color.Transparent, Shape10)
|
||||
.hoverable(creditsSrc).clickable(interactionSource = creditsSrc, indication = null, onClick = a.onCredits)
|
||||
.padding(horizontal = 10.dp, vertical = 8.dp),
|
||||
)
|
||||
val register: (String, LayoutCoordinates) -> Unit = { key, c ->
|
||||
val o = c.positionInRoot(); val r = Rect(o.x, o.y, o.x + c.size.width, o.y + c.size.height)
|
||||
if (positions[key] != r) positions[key] = r
|
||||
}
|
||||
val unregister: (String) -> Unit = { positions.remove(it) }
|
||||
Column(modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
if (s.isHomeApp) {
|
||||
NavItem("Android apps", "android-apps", selected == "android-apps", count = s.androidApps.size,
|
||||
register = register, unregister = unregister) { onSelect("android-apps") }
|
||||
}
|
||||
NavItem("Steam", "steam", selected == "steam", count = s.steamGames.size, register = register, unregister = unregister) { onSelect("steam") }
|
||||
NavItem("Desktop", "desktop", selected == "desktop", count = s.emulators.count { it.installed }, register = register, unregister = unregister) { onSelect("desktop") }
|
||||
NavItem("Setup", "setup", selected == "setup", register = register, unregister = unregister) { onSelect("setup") }
|
||||
Spacer(Modifier.height(6.dp))
|
||||
}
|
||||
}
|
||||
|
||||
// Outlined when a controller is on it, like every other control; the ripple alone was
|
||||
// a faint box on the dark rail.
|
||||
val creditsSrc = remember { MutableInteractionSource() }
|
||||
val creditsHot = rememberHot(creditsSrc)
|
||||
Text(
|
||||
"credits", fontSize = 11.sp, color = if (creditsHot) LocalPalette.current.signal else colors.onSurfaceVariant,
|
||||
modifier = Modifier.clip(Shape10)
|
||||
.border(2.dp, if (creditsHot) LocalPalette.current.signal else Color.Transparent, Shape10)
|
||||
.hoverable(creditsSrc).clickable(interactionSource = creditsSrc, indication = null, onClick = a.onCredits)
|
||||
.padding(horizontal = 10.dp, vertical = 8.dp),
|
||||
BuildStatus(
|
||||
label = s.buildLabel,
|
||||
onCheckLatest = a.onCheckLatestBuild,
|
||||
modifier = Modifier.align(Alignment.Start).padding(start = 8.dp, top = 4.dp, bottom = 2.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -471,13 +481,6 @@ private fun Pane(s: FrontEndState, selected: String, a: FrontEndActions, page: (
|
||||
},
|
||||
label = "pane",
|
||||
) { key -> if (page != null && key == s.pageKey) page() else Content(s, key, a, Modifier.fillMaxSize(), onSelect) }
|
||||
if (page == null && s.pageKey == null && selected == "steam") {
|
||||
BuildStatus(
|
||||
label = s.buildLabel,
|
||||
onCheckLatest = a.onCheckLatestBuild,
|
||||
modifier = Modifier.align(Alignment.TopEnd).padding(12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,12 +492,10 @@ private fun BuildStatus(label: String, onCheckLatest: () -> Unit, modifier: Modi
|
||||
val hot = rememberHot(interaction)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(9.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = modifier
|
||||
.widthIn(max = 360.dp)
|
||||
.clip(Shape12)
|
||||
.background(colors.surface.copy(alpha = 0.96f))
|
||||
.border(1.5.dp, if (hot) pal.signal else pal.line2, Shape12)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.semantics { contentDescription = "Build $label. Check for newest build." }
|
||||
.hoverable(interaction)
|
||||
.clickable(
|
||||
interactionSource = interaction,
|
||||
@@ -502,11 +503,11 @@ private fun BuildStatus(label: String, onCheckLatest: () -> Unit, modifier: Modi
|
||||
role = Role.Button,
|
||||
onClick = onCheckLatest,
|
||||
)
|
||||
.padding(horizontal = 11.dp, vertical = 7.dp),
|
||||
.heightIn(min = 40.dp)
|
||||
.padding(horizontal = 8.dp),
|
||||
) {
|
||||
Text(label, modifier = Modifier.weight(1f), fontSize = 12.sp, fontWeight = FontWeight.SemiBold, color = colors.onBackground, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
Icon(Icons.Filled.Refresh, contentDescription = null, tint = if (hot) pal.signal else colors.onSurfaceVariant, modifier = Modifier.size(15.dp))
|
||||
Text("Check latest", fontSize = 11.sp, fontWeight = FontWeight.SemiBold, color = if (hot) pal.signal else colors.onBackground, maxLines = 1)
|
||||
Text(label, fontSize = 10.sp, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Medium, color = if (hot) colors.onSurfaceVariant else colors.onSurfaceVariant.copy(alpha = 0.72f), maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
Icon(Icons.Filled.Refresh, contentDescription = null, tint = if (hot) pal.signal else colors.onSurfaceVariant.copy(alpha = 0.72f), modifier = Modifier.size(12.dp))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user