From 3dbc4ecc6d6d3d8ff4483ba8dd4aab0f93fc8f4c Mon Sep 17 00:00:00 2001 From: The412Banner Date: Thu, 8 Oct 2026 22:56:38 -0400 Subject: [PATCH] Stores: shelf titles only; the chip row keeps its own ground The shelves lose their subtitle lines. The chip row sits on the page's background with room under it, and the content scrolling beneath is clipped there, with a short fade where it meets the row. --- .../com/droiddeck/launcher/ui/StoresPage.kt | 31 ++++++++++++------- app/src/main/res/values/strings.xml | 5 --- docs/progress-log.md | 3 ++ 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/droiddeck/launcher/ui/StoresPage.kt b/app/src/main/java/com/droiddeck/launcher/ui/StoresPage.kt index db9d12f21..37f361229 100644 --- a/app/src/main/java/com/droiddeck/launcher/ui/StoresPage.kt +++ b/app/src/main/java/com/droiddeck/launcher/ui/StoresPage.kt @@ -142,8 +142,13 @@ internal fun StoresPage(s: FrontEndState, a: FrontEndActions, modifier: Modifier onNext = { if (openGame == null && chip != DOWNLOADS) switchTab(TABS[(TABS.indexOf(tab) + 1) % TABS.size]) }, ), ) { - Rise(0) { StoreChips(chip, s.storeDownloadsActive, onPick = pickChip, onSettings = { settings = true }) } - Column(modifier = Modifier.fillMaxWidth().verticalScroll(scroll).padding(top = 10.dp, bottom = 16.dp)) { + // The chip row stays put on the page's own ground; what scrolls passes under it, clipped, + // with a short fade where it meets the row. + Box(Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.background).padding(bottom = 8.dp)) { + Rise(0) { StoreChips(chip, s.storeDownloadsActive, onPick = pickChip, onSettings = { settings = true }) } + } + Box(Modifier.fillMaxWidth().weight(1f, fill = false).clipToBounds()) { + Column(modifier = Modifier.fillMaxWidth().verticalScroll(scroll).padding(top = 4.dp, bottom = 16.dp)) { when { chip == DOWNLOADS -> StoresDownloadsPane(s, a) store == null -> {} @@ -154,6 +159,11 @@ internal fun StoresPage(s: FrontEndState, a: FrontEndActions, modifier: Modifier else -> Storefront(store, tab, query, s, a, onTab = { switchTab(it) }, onQuery = { query = it }, onOpen = { open(it) }) } } + if (scroll.value > 0) Spacer( + Modifier.align(Alignment.TopCenter).fillMaxWidth().height(14.dp) + .background(Brush.verticalGradient(0f to MaterialTheme.colorScheme.background, 1f to Color.Transparent)), + ) + } } if (settings) StoresSettingsDialog(s, a) { settings = false } StoresState.pendingInstall?.let { item -> InstallWhereDialog(item) { StoresState.pendingInstall = null } } @@ -332,11 +342,11 @@ private fun Storefront( else -> { if (q.isNotEmpty()) Grid(everything.filter { matches(it) && shown(it) }, stringResource(R.string.stores_nothing_matches), card) else { - Shelf(stringResource(R.string.stores_shelf_new), stringResource(R.string.stores_shelf_new_hint), shelf(shelves?.whatsNew), card) - Shelf(stringResource(R.string.stores_shelf_deals), stringResource(R.string.stores_shelf_deals_hint), shelf(shelves?.deals), card) - Shelf(stringResource(R.string.stores_shelf_free), stringResource(R.string.stores_shelf_free_hint), shelf(shelves?.free), card) - Shelf(stringResource(R.string.stores_shelf_trending), stringResource(R.string.stores_shelf_trending_hint), shelf(shelves?.trending), card) - Shelf(stringResource(R.string.stores_shelf_library), stringResource(R.string.stores_shelf_library_hint, store.label), library, card) + Shelf(stringResource(R.string.stores_shelf_new), shelf(shelves?.whatsNew), card) + Shelf(stringResource(R.string.stores_shelf_deals), shelf(shelves?.deals), card) + Shelf(stringResource(R.string.stores_shelf_free), shelf(shelves?.free), card) + Shelf(stringResource(R.string.stores_shelf_trending), shelf(shelves?.trending), card) + Shelf(stringResource(R.string.stores_shelf_library), library, card) if (shelves == null && library.isEmpty()) Rise(4) { Note(if (StoresState.status[store] != null) stringResource(R.string.stores_library_loading) else stringResource(R.string.stores_shelves_loading)) } else if (shelves != null && shelves.isEmpty && store == Store.AMAZON) Rise(4) { Note(stringResource(R.string.stores_amazon_no_catalog)) } } @@ -400,13 +410,10 @@ private fun SearchField(query: String, onQuery: (String) -> Unit, placeholder: S /** A horizontal row of cards under a title; nothing at all when the list is empty or not here yet. */ @Composable -private fun Shelf(title: String, hint: String, items: List?, card: @Composable (CatalogItem) -> Unit) { +private fun Shelf(title: String, items: List?, card: @Composable (CatalogItem) -> Unit) { if (items.isNullOrEmpty()) return val colors = MaterialTheme.colorScheme - Row(verticalAlignment = Alignment.Bottom, horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.fillMaxWidth().padding(top = 14.dp, bottom = 6.dp)) { - Text(title, fontSize = 16.sp, fontWeight = FontWeight.Bold, color = colors.onBackground) - Text(hint, fontSize = 12.sp, color = colors.onSurfaceVariant, modifier = Modifier.padding(bottom = 1.dp)) - } + Text(title, fontSize = 16.sp, fontWeight = FontWeight.Bold, color = colors.onBackground, modifier = Modifier.padding(top = 14.dp, bottom = 6.dp)) Row(horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(vertical = 2.dp)) { for (item in items.take(24)) key(item.key) { Box(Modifier.width(CardWidth)) { card(item) } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2e223a6d1..cc3d0971c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1996,15 +1996,10 @@ Loading the store… No catalog What\'s new - Just landed Deals - On sale this week Free games - Claim and keep Trending - Popular right now Your library - Owned on %1$s Play Install Resume install diff --git a/docs/progress-log.md b/docs/progress-log.md index 907047407..f0046f0f0 100644 --- a/docs/progress-log.md +++ b/docs/progress-log.md @@ -144,6 +144,9 @@ without it and Setup says "Stores engine not built"). (`token`, `__token__`, `f_token`, `hdnts`, access/refresh/id tokens, `code=`) and Authorization / Cookie headers are blanked outside URLs too. `StoreLogTest` covers a GOG secure link and an Epic Akamai URL. The engine's own logcat lines (tag `EpicNative` etc.) are written on the native side. +- **Shelves are titles only** (no "Just landed" / "On sale this week" lines); the chip row sits on + the page's own ground with room under it, and what scrolls passes beneath it clipped, with a short + fade where it meets the row. - **Show mature content** (Setup › Stores and the cog; off by default): the storefront's shelves and search leave out titles the store itself rates or tags as adult; Library, Installed and owned games always show, and a title without rating data stays. GOG: `ratings[].ageRating` 17+ (ESRB M