Library rows: circular teal play button at row left (Android-home affordance) — plays/stops without opening the detail page; DONE files only; Stop square while playing
This commit is contained in:
parent
d362dc6f42
commit
13aae7818d
2 changed files with 61 additions and 0 deletions
|
|
@ -40,6 +40,11 @@ import androidx.compose.ui.semantics.contentDescription
|
||||||
import androidx.compose.ui.semantics.semantics
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
|
import androidx.compose.material.icons.filled.Stop
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.focus.onFocusEvent
|
import androidx.compose.ui.focus.onFocusEvent
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
@ -165,6 +170,43 @@ fun ShonarGhostButton(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Circular transport button, matching the Android home rows: teal
|
||||||
|
* hairline ring + teal play triangle; swaps to a filled-look stop
|
||||||
|
* square while its row is playing. Inner clickable consumes the tap,
|
||||||
|
* so the enclosing card's open-on-click doesn't fire. */
|
||||||
|
@Composable
|
||||||
|
fun ShonarPlayCircle(
|
||||||
|
playing: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
size: androidx.compose.ui.unit.Dp = 40.dp,
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = modifier.size(size)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
.border(Shonar.BorderW, Shonar.Accent.copy(alpha = 0.7f), CircleShape),
|
||||||
|
color = androidx.compose.ui.graphics.Color.Transparent,
|
||||||
|
shape = CircleShape,
|
||||||
|
) {
|
||||||
|
Box(contentAlignment = Alignment.Center) {
|
||||||
|
if (playing) {
|
||||||
|
Icon(Icons.Filled.Stop, contentDescription = "Stop",
|
||||||
|
tint = Shonar.Accent,
|
||||||
|
modifier = Modifier.size(Shonar.IconSm))
|
||||||
|
} else {
|
||||||
|
Icon(Icons.Filled.PlayArrow, contentDescription = "Play",
|
||||||
|
tint = Shonar.Accent,
|
||||||
|
modifier = Modifier.size(Shonar.IconSm))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Destructive action: quiet red outline — visible without shouting.
|
/** Destructive action: quiet red outline — visible without shouting.
|
||||||
* ink overrides the label/icon color (detail-page Play/Delete use
|
* ink overrides the label/icon color (detail-page Play/Delete use
|
||||||
* TextPrimary = white-on-dark per user directive, Sep 21). */
|
* TextPrimary = white-on-dark per user directive, Sep 21). */
|
||||||
|
|
|
||||||
|
|
@ -423,6 +423,25 @@ fun LibraryScreen(state: DesktopState) {
|
||||||
checked = isSelected,
|
checked = isSelected,
|
||||||
onCheckedChange = { state.toggleSelected(e.file.absolutePath) },
|
onCheckedChange = { state.toggleSelected(e.file.absolutePath) },
|
||||||
)
|
)
|
||||||
|
// Android-home affordance: circular play at the
|
||||||
|
// left of the row — listen without opening.
|
||||||
|
// Transcribed files only (nothing to hear yet
|
||||||
|
// otherwise); shows Stop while playing.
|
||||||
|
val playingFile by state.playingFile.collectAsState()
|
||||||
|
val isThisPlaying =
|
||||||
|
playingFile?.absolutePath == e.file.absolutePath
|
||||||
|
if (e.status == FileStatus.DONE) {
|
||||||
|
Spacer(Modifier.width(Shonar.Space1))
|
||||||
|
ShonarPlayCircle(
|
||||||
|
playing = isThisPlaying,
|
||||||
|
onClick = {
|
||||||
|
if (isThisPlaying) state.stopPlayback()
|
||||||
|
else state.playFrom(e.file)
|
||||||
|
})
|
||||||
|
Spacer(Modifier.width(Shonar.Space2))
|
||||||
|
} else {
|
||||||
|
Spacer(Modifier.width(Shonar.Space2))
|
||||||
|
}
|
||||||
Column(Modifier.weight(1f)) {
|
Column(Modifier.weight(1f)) {
|
||||||
Text(e.file.nameWithoutExtension,
|
Text(e.file.nameWithoutExtension,
|
||||||
style = MaterialTheme.typography.titleSmall,
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue