mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
Stores: the Library dropdown opens under its tab, as wide as its entries
The dropdown used the menus' right-aligned placement and their 220 dp minimum, so under a tab near the left it was pushed to the edge over the rail. AnchoredMenu has a compact mode now - under its anchor, left edges aligned, as wide as its entries between the tab's width and 260 dp - which the Library dropdown uses.
This commit is contained in:
1 parent
6626cf99b7
commit
f5c343bde0
3 files changed
+33
-11
No files matched your search
@@ -233,6 +233,19 @@ class MenuHost {
|
||||
@Composable
|
||||
fun rememberMenuHost(): MenuHost = remember { MenuHost() }
|
||||
|
||||
/** Under the anchor with left edges aligned, kept on screen: a dropdown that belongs to a tab. */
|
||||
private class BelowStartProvider(private val gap: Int) : PopupPositionProvider {
|
||||
override fun calculatePosition(anchorBounds: IntRect, windowSize: IntSize, layoutDirection: LayoutDirection, popupContentSize: IntSize): IntOffset {
|
||||
var x = anchorBounds.left
|
||||
if (x + popupContentSize.width > windowSize.width - 8) x = windowSize.width - 8 - popupContentSize.width
|
||||
if (x < 8) x = 8
|
||||
var y = anchorBounds.bottom + gap
|
||||
if (y + popupContentSize.height > windowSize.height - 8) y = anchorBounds.top - gap - popupContentSize.height
|
||||
if (y < 8) y = 8
|
||||
return IntOffset(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
private class BelowEndProvider(private val gap: Int) : PopupPositionProvider {
|
||||
override fun calculatePosition(anchorBounds: IntRect, windowSize: IntSize, layoutDirection: LayoutDirection, popupContentSize: IntSize): IntOffset {
|
||||
var x = anchorBounds.right - popupContentSize.width
|
||||
@@ -245,7 +258,12 @@ private class BelowEndProvider(private val gap: Int) : PopupPositionProvider {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AnchoredMenu(open: Boolean, onDismiss: () -> Unit, title: String? = null, note: String? = null, content: @Composable ColumnScope.(FocusRequester) -> Unit) {
|
||||
fun AnchoredMenu(
|
||||
open: Boolean, onDismiss: () -> Unit, title: String? = null, note: String? = null,
|
||||
/** A tab's dropdown: under its anchor, left edges aligned, as wide as its entries (within [minWidth]..260 dp). */
|
||||
compact: Boolean = false, minWidth: androidx.compose.ui.unit.Dp = 120.dp,
|
||||
content: @Composable ColumnScope.(FocusRequester) -> Unit,
|
||||
) {
|
||||
val state = remember { MutableTransitionState(false) }
|
||||
state.targetState = open
|
||||
if (!state.currentState && !state.targetState && state.isIdle) return
|
||||
@@ -259,14 +277,14 @@ fun AnchoredMenu(open: Boolean, onDismiss: () -> Unit, title: String? = null, no
|
||||
}
|
||||
}
|
||||
val gap = with(LocalDensity.current) { 6.dp.roundToPx() }
|
||||
val provider = remember(gap) { BelowEndProvider(gap) }
|
||||
val provider = remember(gap, compact) { if (compact) BelowStartProvider(gap) else BelowEndProvider(gap) }
|
||||
val colors = MaterialTheme.colorScheme
|
||||
val pal = LocalPalette.current
|
||||
Popup(popupPositionProvider = provider, onDismissRequest = onDismiss, properties = PopupProperties(focusable = true)) {
|
||||
AnimatedVisibility(
|
||||
visibleState = state,
|
||||
enter = fadeIn(Motion.tw(180)) + scaleIn(Motion.sp(0.7f), initialScale = 0.94f, transformOrigin = TransformOrigin(1f, 0f)),
|
||||
exit = fadeOut(Motion.tw(140)) + scaleOut(Motion.tw(140), targetScale = 0.96f, transformOrigin = TransformOrigin(1f, 0f)),
|
||||
enter = fadeIn(Motion.tw(180)) + scaleIn(Motion.sp(0.7f), initialScale = 0.94f, transformOrigin = TransformOrigin(if (compact) 0f else 1f, 0f)),
|
||||
exit = fadeOut(Motion.tw(140)) + scaleOut(Motion.tw(140), targetScale = 0.96f, transformOrigin = TransformOrigin(if (compact) 0f else 1f, 0f)),
|
||||
label = "menu",
|
||||
) {
|
||||
// A long list (a driver menu with its downloads) must not run off the screen: the menu is
|
||||
@@ -274,7 +292,7 @@ fun AnchoredMenu(open: Boolean, onDismiss: () -> Unit, title: String? = null, no
|
||||
val maxHeight = (androidx.compose.ui.platform.LocalConfiguration.current.screenHeightDp * 0.86f).dp
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.widthIn(min = 220.dp, max = 340.dp)
|
||||
.then(if (compact) Modifier.widthIn(min = minWidth, max = 260.dp).width(androidx.compose.foundation.layout.IntrinsicSize.Max) else Modifier.widthIn(min = 220.dp, max = 340.dp))
|
||||
.heightIn(max = maxHeight)
|
||||
.shadow(24.dp, RowShape, ambientColor = Color.Black, spotColor = Color.Black)
|
||||
.clip(RowShape)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.draw.drawWithContent
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
@@ -333,8 +334,8 @@ private fun Storefront(
|
||||
listOf("store" to stringResource(R.string.stores_tab_store), "library" to libraryLabel).filter { it.first in tabsFor(store) },
|
||||
tab,
|
||||
onPick = { key -> if (key == "library") { if (tab == "library") filterOpen = !filterOpen else onTab(key) } else onTab(key) },
|
||||
menuFor = { key ->
|
||||
if (key == "library") AnchoredMenu(filterOpen, onDismiss = { filterOpen = false }) { first ->
|
||||
menuFor = { key, tabWidth ->
|
||||
if (key == "library") AnchoredMenu(filterOpen, onDismiss = { filterOpen = false }, compact = true, minWidth = tabWidth) { first ->
|
||||
MenuItem(stringResource(R.string.stores_tab_count, stringResource(R.string.stores_tab_all), library.size), checked = !installedOnly, focusRequester = first) { installedOnly = false; filterOpen = false; onTab("library") }
|
||||
MenuItem(stringResource(R.string.stores_tab_count, stringResource(R.string.stores_tab_installed), installedItems.size), checked = installedOnly) { installedOnly = true; filterOpen = false; onTab("library") }
|
||||
}
|
||||
@@ -386,7 +387,7 @@ private fun Storefront(
|
||||
@Composable
|
||||
private fun SubTabs(
|
||||
tabs: List<Pair<String, String>>, selected: String, onPick: (String) -> Unit,
|
||||
menuFor: @Composable (String) -> Unit = {}, trailing: @Composable () -> Unit,
|
||||
menuFor: @Composable (String, androidx.compose.ui.unit.Dp) -> Unit = { _, _ -> }, trailing: @Composable () -> Unit,
|
||||
) {
|
||||
val colors = MaterialTheme.colorScheme
|
||||
val pal = LocalPalette.current
|
||||
@@ -398,7 +399,9 @@ private fun SubTabs(
|
||||
val src = remember { MutableInteractionSource() }
|
||||
val hot = rememberHot(src)
|
||||
val pick = { onPick(key) }
|
||||
Box {
|
||||
val density = androidx.compose.ui.platform.LocalDensity.current
|
||||
var width by remember { mutableStateOf(0.dp) }
|
||||
Box(Modifier.onSizeChanged { width = with(density) { it.width.toDp() } }) {
|
||||
Column(
|
||||
modifier = Modifier.paneItem("storetab:$key").clip(RoundedCornerShape(8.dp))
|
||||
.background(if (hot) pal.signal.copy(alpha = 0.12f) else Color.Transparent)
|
||||
@@ -411,7 +414,7 @@ private fun SubTabs(
|
||||
Box(Modifier.padding(top = 4.dp).fillMaxWidth().height(2.dp).background(if (on) pal.signal else Color.Transparent))
|
||||
}
|
||||
// A tab's own dropdown (Library's filter), anchored under it.
|
||||
menuFor(key)
|
||||
menuFor(key, width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,8 @@ without it and Setup says "Stores engine not built").
|
||||
GOG Library 33 / All 120): Library's dropdown filters All (every owned game, the default, the
|
||||
tab's count) or Installed, and the tab reads the filter ("Library (33)", "Installed (1)"); the old
|
||||
All view is gone. Amazon has Library alone. Search follows the filter; the shoulders cycle Store
|
||||
and Library.
|
||||
and Library. The dropdown opens under the Library tab, left edges aligned, as wide as its
|
||||
entries (at least the tab, at most 260 dp) - `AnchoredMenu(compact = true)`.
|
||||
- **The Stores chip row paints nothing** (maintainer: a black box behind the chips): it sits over the
|
||||
page's own background; the content scrolling under it is clipped at its edge and faded there by
|
||||
the content's alpha once scrolled, never by a colour.
|
||||
|
||||
Reference in new issue
Block a user