fix: folder tab long-press via Initial-pass pointerInput watcher + shared Handler timer; taps still select (verified on-device: 8-step gesture suite)
This commit is contained in:
parent
3f84249c82
commit
a99d4c43f4
1 changed files with 48 additions and 11 deletions
|
|
@ -4,7 +4,8 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.awaitEachGesture
|
||||||
|
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
@ -53,17 +54,20 @@ import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.input.pointer.PointerEventPass
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
@ -178,6 +182,18 @@ fun LibraryScreen(
|
||||||
Icon(Icons.Default.Shuffle, contentDescription = "Shuffle play")
|
Icon(Icons.Default.Shuffle, contentDescription = "Shuffle play")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Reliable delete path for the selected folder (besides long-press).
|
||||||
|
if (uiState.selectedPlaylistId != null) {
|
||||||
|
allPlaylists.firstOrNull { it.id == uiState.selectedPlaylistId }?.let { selected ->
|
||||||
|
IconButton(onClick = { playlistToDelete = selected }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Delete,
|
||||||
|
contentDescription = "Delete folder",
|
||||||
|
tint = PremiumTextSecondary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
|
|
@ -208,17 +224,38 @@ fun LibraryScreen(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
|
// One shared long-press timer for the folder tabs. The
|
||||||
|
// per-Tab watcher (pointerInput on the Initial pass) arms
|
||||||
|
// it on DOWN and disarms on UP/CANCEL/move; the Tab's own
|
||||||
|
// selectable keeps handling taps normally on the Main pass.
|
||||||
|
val tabLpHandler = android.os.Handler(android.os.Looper.getMainLooper())
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
onDispose { tabLpHandler.removeCallbacksAndMessages(null) }
|
||||||
|
}
|
||||||
tabLabels.forEachIndexed { index, label ->
|
tabLabels.forEachIndexed { index, label ->
|
||||||
Tab(
|
Tab(
|
||||||
// Long-press detected at the pointer level: putting
|
modifier = if (index == 0) Modifier else Modifier.pointerInput(index) {
|
||||||
// combinedClickable here would be masked by Tab's own
|
awaitEachGesture {
|
||||||
// selectable semantics and break selection entirely.
|
val down = awaitFirstDown(
|
||||||
modifier = Modifier.pointerInput(index) {
|
requireUnconsumed = false,
|
||||||
detectTapGestures(
|
pass = PointerEventPass.Initial,
|
||||||
onLongPress = {
|
)
|
||||||
if (index > 0) playlistToDelete = filteredPlaylists[index - 1]
|
val playlist = filteredPlaylists[index - 1]
|
||||||
},
|
val slop = viewConfiguration.touchSlop
|
||||||
)
|
val timer = Runnable { playlistToDelete = playlist }
|
||||||
|
tabLpHandler.removeCallbacks(timer)
|
||||||
|
tabLpHandler.postDelayed(
|
||||||
|
timer,
|
||||||
|
viewConfiguration.longPressTimeoutMillis,
|
||||||
|
)
|
||||||
|
while (true) {
|
||||||
|
val event = awaitPointerEvent(PointerEventPass.Initial)
|
||||||
|
val change = event.changes.firstOrNull { it.id == down.id }
|
||||||
|
if (change == null || !change.pressed) break
|
||||||
|
if ((change.position - down.position).getDistance() > slop) break
|
||||||
|
}
|
||||||
|
tabLpHandler.removeCallbacks(timer)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
selected = selectedTabIndex == index,
|
selected = selectedTabIndex == index,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue