new: add adjustable subtitle text size in player and preferences UI
This commit is contained in:
@@ -72,6 +72,8 @@ fun PlayerScreen(
|
||||
onNextSentenceClicked = viewModel::goToNextSentence,
|
||||
onShowTranscriptionChange = viewModel::setShowTranscription,
|
||||
onShowTranslationChange = viewModel::setShowTranslation,
|
||||
onTextSizeDecrease = viewModel::decreaseSubtitleTextSize,
|
||||
onTextSizeIncrease = viewModel::increaseSubtitleTextSize,
|
||||
onSpeedChange = viewModel::setPlaybackSpeed,
|
||||
onBackClicked = onBackClicked,
|
||||
)
|
||||
@@ -88,6 +90,8 @@ fun PlayerScreen(
|
||||
onNextSentenceClicked: () -> Unit = {},
|
||||
onShowTranscriptionChange: (Boolean) -> Unit = {},
|
||||
onShowTranslationChange: (Boolean) -> Unit = {},
|
||||
onTextSizeDecrease: () -> Unit = {},
|
||||
onTextSizeIncrease: () -> Unit = {},
|
||||
onSpeedChange: (Float) -> Unit = {},
|
||||
onBackClicked: () -> Unit = {},
|
||||
) {
|
||||
@@ -184,6 +188,8 @@ fun PlayerScreen(
|
||||
subtitlePreferences = uiState.subtitlePreferences,
|
||||
onShowTranscriptionChange = onShowTranscriptionChange,
|
||||
onShowTranslationChange = onShowTranslationChange,
|
||||
onTextSizeDecrease = onTextSizeDecrease,
|
||||
onTextSizeIncrease = onTextSizeIncrease,
|
||||
onDismiss = { showPreferences = false },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -27,9 +28,11 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import fr.ajaury.gwenedeg.core.model.Phrase
|
||||
@@ -90,23 +93,34 @@ fun SubtitleList(
|
||||
}
|
||||
}
|
||||
|
||||
val density = LocalDensity.current
|
||||
BoxWithConstraints(modifier = modifier) {
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
// Half-height padding so the first and last lines can reach the vertical center.
|
||||
contentPadding = PaddingValues(vertical = maxHeight / 2),
|
||||
// Half-height padding so the first and last lines can reach the vertical center. Captured here
|
||||
// because `maxHeight` (BoxWithConstraintsScope) is not in scope inside the provider lambda.
|
||||
val verticalPadding = maxHeight / 2
|
||||
// Scale only the subtitle fonts by overriding the density's fontScale (dp layout is untouched).
|
||||
CompositionLocalProvider(
|
||||
LocalDensity provides Density(
|
||||
density = density.density,
|
||||
fontScale = density.fontScale * subtitlePreferences.textScale,
|
||||
),
|
||||
) {
|
||||
itemsIndexed(lines) { index, line ->
|
||||
val isCurrent = index == centeredIndex
|
||||
Subtitle(
|
||||
line = line,
|
||||
isCurrent = isCurrent,
|
||||
subtitlePreferences = subtitlePreferences,
|
||||
onClick = { onSeek(index) },
|
||||
)
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
contentPadding = PaddingValues(vertical = verticalPadding),
|
||||
) {
|
||||
itemsIndexed(lines) { index, line ->
|
||||
val isCurrent = index == centeredIndex
|
||||
Subtitle(
|
||||
line = line,
|
||||
isCurrent = isCurrent,
|
||||
subtitlePreferences = subtitlePreferences,
|
||||
onClick = { onSeek(index) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
@@ -9,6 +9,7 @@ import fr.ajaury.gwenedeg.player.domain.PlaybackRepository
|
||||
import fr.ajaury.gwenedeg.player.model.PlaybackState
|
||||
import fr.ajaury.gwenedeg.preferences.domain.PreferencesRepository
|
||||
import fr.ajaury.gwenedeg.preferences.model.PlaybackPreferences
|
||||
import fr.ajaury.gwenedeg.preferences.model.SubtitlePreferences
|
||||
import fr.ajaury.gwenedeg.records.domain.RecordRepository
|
||||
import fr.ajaury.gwenedeg.records.model.Record
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -125,6 +126,22 @@ class PlayerViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun increaseSubtitleTextSize() {
|
||||
val current = uiState.value.subtitlePreferences.textScale
|
||||
setSubtitleTextScale(SubtitlePreferences.nextTextScale(current))
|
||||
}
|
||||
|
||||
fun decreaseSubtitleTextSize() {
|
||||
val current = uiState.value.subtitlePreferences.textScale
|
||||
setSubtitleTextScale(SubtitlePreferences.previousTextScale(current))
|
||||
}
|
||||
|
||||
private fun setSubtitleTextScale(scale: Float) {
|
||||
viewModelScope.launch {
|
||||
preferencesRepository.setSubtitleTextScale(scale)
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
playbackRepository.stop()
|
||||
}
|
||||
|
||||
+71
@@ -2,27 +2,38 @@ package fr.ajaury.gwenedeg.preferences.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Remove
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fr.ajaury.gwenedeg.preferences.model.SubtitlePreferences
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.Res
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.subtitle_preferences_title
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.subtitle_text_size_decrease
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.subtitle_text_size_increase
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.subtitle_text_size_label
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.subtitle_transcription_description
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.subtitle_transcription_label
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.subtitle_translation_description
|
||||
import fr.ajaury.gwenedeg.resources.generated.resources.subtitle_translation_label
|
||||
import fr.ajaury.gwenedeg.theme.ChomBevTheme
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Bottom sheet letting the user toggle the display of the Breton transcription (BZH) and the
|
||||
@@ -34,6 +45,8 @@ fun SubtitlePreferencesBottomSheet(
|
||||
subtitlePreferences: SubtitlePreferences,
|
||||
onShowTranscriptionChange: (Boolean) -> Unit = {},
|
||||
onShowTranslationChange: (Boolean) -> Unit = {},
|
||||
onTextSizeDecrease: () -> Unit = {},
|
||||
onTextSizeIncrease: () -> Unit = {},
|
||||
onDismiss: () -> Unit = {},
|
||||
sheetState: SheetState = rememberModalBottomSheetState(),
|
||||
) {
|
||||
@@ -45,6 +58,8 @@ fun SubtitlePreferencesBottomSheet(
|
||||
subtitlePreferences = subtitlePreferences,
|
||||
onShowTranscriptionChange = onShowTranscriptionChange,
|
||||
onShowTranslationChange = onShowTranslationChange,
|
||||
onTextSizeDecrease = onTextSizeDecrease,
|
||||
onTextSizeIncrease = onTextSizeIncrease,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -54,6 +69,8 @@ private fun SubtitlePreferencesContent(
|
||||
subtitlePreferences: SubtitlePreferences,
|
||||
onShowTranscriptionChange: (Boolean) -> Unit = {},
|
||||
onShowTranslationChange: (Boolean) -> Unit = {},
|
||||
onTextSizeDecrease: () -> Unit = {},
|
||||
onTextSizeIncrease: () -> Unit = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -78,9 +95,63 @@ private fun SubtitlePreferencesContent(
|
||||
checked = subtitlePreferences.showTranslation,
|
||||
onCheckedChange = onShowTranslationChange,
|
||||
)
|
||||
SubtitleTextSizeControl(
|
||||
textScale = subtitlePreferences.textScale,
|
||||
onDecrease = onTextSizeDecrease,
|
||||
onIncrease = onTextSizeIncrease,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubtitleTextSizeControl(
|
||||
textScale: Float,
|
||||
onDecrease: () -> Unit = {},
|
||||
onIncrease: () -> Unit = {},
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.subtitle_text_size_label),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
IconButton(
|
||||
onClick = onDecrease,
|
||||
enabled = textScale > SubtitlePreferences.TEXT_SCALE_STEPS.first(),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Remove,
|
||||
contentDescription = stringResource(Res.string.subtitle_text_size_decrease),
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = formatTextScale(textScale),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
IconButton(
|
||||
onClick = onIncrease,
|
||||
enabled = textScale < SubtitlePreferences.TEXT_SCALE_STEPS.last(),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = stringResource(Res.string.subtitle_text_size_increase),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Formats a text-scale multiplier as a short label, e.g. 1.0 -> "x1", 1.2 -> "x1.2". */
|
||||
private fun formatTextScale(scale: Float): String {
|
||||
val tenths = (scale * 10).roundToInt()
|
||||
return if (tenths % 10 == 0) "x${tenths / 10}" else "x${tenths / 10}.${tenths % 10}"
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SubtitlePreferencesContentPreview() {
|
||||
|
||||
Reference in New Issue
Block a user