media: the player card with its cover sits beside every tab, Up next comes after Now playing, and Browse's header gets room

This commit is contained in:
Seb3thehacker committed 2026-10-26 01:25:45 -07:00
1 parent 15d6721705
commit 324112ca8b
2 files changed
+30 -26

No files matched your search

@@ -84,14 +84,15 @@ internal fun BrowsePanel(browse: app.seb3thehacker.gearslip.media.BrowseState, m
// row could say that the app's name in the top-left corner hasn't already - showing it
// again here (as "Browse", or as the app's own name) was just the same label twice.
if (media.canGoUp || browse.trail.isNotEmpty()) {
Row(verticalAlignment = Alignment.CenterVertically) {
Row(Modifier.padding(bottom = 12.dp), verticalAlignment = Alignment.CenterVertically) {
if (media.canGoUp) {
GsIconButton(Icons.Filled.ArrowBack, "Up", media::up)
GsIconButton(Icons.Filled.ArrowBack, "Up", media::up, size = 44.dp, shape = CircleShape)
Spacer(Modifier.width(14.dp))
}
browse.trail.lastOrNull()?.let {
Text(
it,
style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold,
style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold,
maxLines = 1, overflow = TextOverflow.Ellipsis,
)
}
@@ -251,9 +252,9 @@ private fun QueueRow(track: QueueTrack, media: CarMedia) {
color = MaterialTheme.colorScheme.surfaceContainer,
modifier = Modifier.fillMaxWidth(),
) {
Row(Modifier.padding(10.dp), verticalAlignment = Alignment.CenterVertically) {
Row(Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically) {
RowArt(icon, MediaIcons.MusicNote)
Column(Modifier.weight(1f).padding(horizontal = 12.dp)) {
Column(Modifier.weight(1f).padding(horizontal = 14.dp)) {
Text(track.title, style = MaterialTheme.typography.titleMedium, maxLines = 1, overflow = TextOverflow.Ellipsis)
if (track.subtitle.isNotEmpty()) {
Text(track.subtitle, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis)
@@ -276,9 +277,9 @@ internal fun EntryRow(entry: MediaEntry, media: CarMedia, onClick: () -> Unit =
color = MaterialTheme.colorScheme.surfaceContainer,
modifier = Modifier.fillMaxWidth(),
) {
Row(Modifier.padding(10.dp), verticalAlignment = Alignment.CenterVertically) {
Row(Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically) {
RowArt(icon, if (entry.browsable) MediaIcons.Folder else MediaIcons.MusicNote)
Column(Modifier.weight(1f).padding(horizontal = 12.dp)) {
Column(Modifier.weight(1f).padding(horizontal = 14.dp)) {
Text(entry.title, style = MaterialTheme.typography.titleMedium, maxLines = 1, overflow = TextOverflow.Ellipsis)
if (entry.subtitle.isNotEmpty()) {
Text(entry.subtitle, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, maxLines = 1, overflow = TextOverflow.Ellipsis)
@@ -296,7 +297,7 @@ internal fun EntryRow(entry: MediaEntry, media: CarMedia, onClick: () -> Unit =
@Composable
private fun RowArt(icon: android.graphics.Bitmap?, fallback: ImageVector) {
Box(
Modifier.size(62.dp).clip(MaterialTheme.shapes.medium).background(MaterialTheme.colorScheme.surfaceContainerHigh),
Modifier.size(56.dp).clip(RoundedCornerShape(12.dp)).background(MaterialTheme.colorScheme.surfaceContainerHigh),
contentAlignment = Alignment.Center,
) {
if (icon != null) Image(icon.asImageBitmap(), null, Modifier.fillMaxSize(), contentScale = ContentScale.Crop)
@@ -175,14 +175,14 @@ fun MediaScreen(app: MediaApp, onExit: () -> Unit) {
if (tab == Tab.NOW_PLAYING) {
CoverArt(art, Modifier.weight(1f).fillMaxWidth())
} else {
CompactPlayer(now, media, Modifier.weight(1f).fillMaxWidth())
CompactPlayer(now, media, art, Modifier.weight(1f).fillMaxWidth())
}
}
Column(Modifier.weight(1.2f).fillMaxHeight()) {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
MediaTab("Now playing", tab == Tab.NOW_PLAYING, Modifier.weight(1f)) { tab = Tab.NOW_PLAYING }
MediaTab("Browse", tab == Tab.BROWSE, Modifier.weight(1f)) { tab = Tab.BROWSE }
MediaTab("Up next", tab == Tab.QUEUE, Modifier.weight(1f)) { tab = Tab.QUEUE }
MediaTab("Browse", tab == Tab.BROWSE, Modifier.weight(1f)) { tab = Tab.BROWSE }
MediaTab("Lyrics", tab == Tab.LYRICS, Modifier.weight(1f)) { tab = Tab.LYRICS }
if (now.canSearch || media.canSearchLibrary) {
GsIconButton(
@@ -603,8 +603,8 @@ internal fun NowPlayingPage(now: NowPlaying, media: CarMedia, modifier: Modifier
/** The cover, as big a square as its space allows, centred in it. */
@Composable
private fun CoverArt(art: android.graphics.Bitmap?, modifier: Modifier) {
BoxWithConstraints(modifier, contentAlignment = Alignment.Center) {
private fun CoverArt(art: android.graphics.Bitmap?, modifier: Modifier, alignment: Alignment = Alignment.Center) {
BoxWithConstraints(modifier, contentAlignment = alignment) {
val shape = RoundedCornerShape(22.dp)
Surface(
shape = shape,
@@ -707,30 +707,33 @@ private fun NowPlayingControls(now: NowPlaying, media: CarMedia, modifier: Modif
}
/**
* The player beside the browse list, the queue and the lyrics: no art, which the list needs the
* room for, just what's playing, the seek bar and the controls.
* The player beside the browse list, the queue and the lyrics: the same card as beside the map on
* Home, the cover taking whatever the title and controls leave, so every tab keeps the song in
* sight and the controls in one place.
*/
@Composable
private fun CompactPlayer(now: NowPlaying, media: CarMedia, modifier: Modifier) {
Column(
modifier.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(14.dp),
) {
Column {
private fun CompactPlayer(now: NowPlaying, media: CarMedia, art: android.graphics.Bitmap?, modifier: Modifier) {
val shape = RoundedCornerShape(24.dp)
Surface(modifier.gsPanelShadow(shape), shape = shape, color = MaterialTheme.colorScheme.surfaceContainer) {
AlbumGlow(art, Modifier.fillMaxSize())
Column(Modifier.fillMaxSize().padding(14.dp), horizontalAlignment = Alignment.CenterHorizontally) {
CoverArt(art, Modifier.weight(1f).fillMaxWidth(), Alignment.BottomCenter)
Spacer(Modifier.height(10.dp))
Text(
now.title.ifEmpty { "Nothing playing" },
style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold,
maxLines = 2, overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold,
maxLines = 1, overflow = TextOverflow.Ellipsis,
)
Text(
now.artist,
style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1, overflow = TextOverflow.Ellipsis,
)
PlayerProgress(now, media, Modifier.fillMaxWidth().padding(vertical = 2.dp), showTimes = false)
TransportRow(now, media, skip = 52.dp, modifier = Modifier.fillMaxWidth())
Spacer(Modifier.height(8.dp))
PlayModeRow(now, media, size = 46.dp, modifier = Modifier.fillMaxWidth())
}
PlayerProgress(now, media, Modifier.fillMaxWidth())
TransportRow(now, media, skip = 52.dp, modifier = Modifier.fillMaxWidth())
PlayModeRow(now, media, size = 48.dp, modifier = Modifier.fillMaxWidth())
}
}