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.
This commit is contained in:
The412Banner committed 2026-10-08 22:56:38 -04:00
1 parent 2f1e0a1348
commit 3dbc4ecc6d
3 files changed
+22 -17

No files matched your search

@@ -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<CatalogItem>?, card: @Composable (CatalogItem) -> Unit) {
private fun Shelf(title: String, items: List<CatalogItem>?, 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) } }
}
-5
View File
@@ -1996,15 +1996,10 @@
<string name="stores_shelves_loading">Loading the store…</string>
<string name="stores_amazon_no_catalog">No catalog</string>
<string name="stores_shelf_new">What\'s new</string>
<string name="stores_shelf_new_hint">Just landed</string>
<string name="stores_shelf_deals">Deals</string>
<string name="stores_shelf_deals_hint">On sale this week</string>
<string name="stores_shelf_free">Free games</string>
<string name="stores_shelf_free_hint">Claim and keep</string>
<string name="stores_shelf_trending">Trending</string>
<string name="stores_shelf_trending_hint">Popular right now</string>
<string name="stores_shelf_library">Your library</string>
<string name="stores_shelf_library_hint">Owned on %1$s</string>
<string name="stores_play">Play</string>
<string name="stores_install">Install</string>
<string name="stores_resume_install">Resume install</string>
+3
View File
@@ -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