Coverage Summary for Class: TimeSignatureEditDialogKt (com.vsevolodganin.clicktrack.ui.piece)

Class Class, % Method, % Branch, % Line, % Instruction, %
TimeSignatureEditDialogKt 0% (0/1) 0% (0/15) 0% (0/50) 0% (0/109) 0% (0/1530)


 package com.vsevolodganin.clicktrack.ui.piece
 
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.EaseOutQuad
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.lazy.LazyItemScope
 import androidx.compose.material.AlertDialog
 import androidx.compose.material.LocalContentColor
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Text
 import androidx.compose.material.TextButton
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.alpha
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.lerp
 import clicktrack.multiplatform.generated.resources.Res
 import clicktrack.multiplatform.generated.resources.general_cancel
 import clicktrack.multiplatform.generated.resources.general_ok
 import clicktrack.multiplatform.generated.resources.time_signature_edit_dialog_title
 import clicktrack.multiplatform.generated.resources.time_signature_note_count_header
 import clicktrack.multiplatform.generated.resources.time_signature_note_value_eighth
 import clicktrack.multiplatform.generated.resources.time_signature_note_value_half
 import clicktrack.multiplatform.generated.resources.time_signature_note_value_header
 import clicktrack.multiplatform.generated.resources.time_signature_note_value_quarter
 import clicktrack.multiplatform.generated.resources.time_signature_note_value_sixteenth
 import clicktrack.multiplatform.generated.resources.time_signature_note_value_thirty_second
 import clicktrack.multiplatform.generated.resources.time_signature_note_value_whole
 import clicktrack.multiplatform.generated.resources.time_signature_reset_to_four_four
 import com.vsevolodganin.clicktrack.model.TimeSignature
 import com.vsevolodganin.clicktrack.ui.theme.ClickTrackTheme
 import org.jetbrains.compose.resources.stringResource
 import org.jetbrains.compose.ui.tooling.preview.Preview
 
 @Composable
 fun TimeSignatureEditDialog(
     value: TimeSignature,
     onValueChange: (TimeSignature) -> Unit,
     onDismissRequest: () -> Unit,
 ) {
     var localValue by remember { mutableStateOf(value) }
         .apply { this.value = value }
 
     AlertDialog(
         onDismissRequest = onDismissRequest,
         title = {
             Text(
                 text = stringResource(Res.string.time_signature_edit_dialog_title),
                 style = MaterialTheme.typography.h6,
             )
         },
         text = {
             TimeSignatureViewDialogContent(
                 value = localValue,
                 onValueChange = { localValue = it },
                 modifier = Modifier.height(200.dp),
             )
         },
         buttons = {
             Box(
                 modifier = Modifier.fillMaxWidth(),
                 contentAlignment = Alignment.BottomEnd,
             ) {
                 Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                     TextButton(onClick = { localValue = TimeSignature(4, 4) }) {
                         Text(stringResource(Res.string.time_signature_reset_to_four_four).uppercase())
                     }
 
                     TextButton(onClick = onDismissRequest) {
                         Text(stringResource(Res.string.general_cancel).uppercase())
                     }
 
                     TextButton(
                         onClick = {
                             onValueChange(localValue)
                             onDismissRequest()
                         },
                     ) {
                         Text(stringResource(Res.string.general_ok).uppercase())
                     }
                 }
             }
         },
     )
 }
 
 @Composable
 private fun TimeSignatureViewDialogContent(
     value: TimeSignature,
     onValueChange: (TimeSignature) -> Unit,
     modifier: Modifier = Modifier,
 ) {
     Row(
         modifier = modifier.fillMaxSize(),
         verticalAlignment = Alignment.CenterVertically,
         horizontalArrangement = Arrangement.spacedBy(32.dp, Alignment.CenterHorizontally),
     ) {
         NoteCountColumn(
             value = value.noteCount,
             onValueChange = {
                 onValueChange(value.copy(noteCount = it))
             },
             modifier = Modifier.weight(1f),
         )
         NoteValueColumn(
             value = value.noteValue,
             onValueChange = {
                 onValueChange(value.copy(noteValue = it))
             },
             modifier = Modifier.weight(1f),
         )
     }
 }
 
 @Composable
 private fun NoteCountColumn(
     value: Int,
     onValueChange: (Int) -> Unit,
     modifier: Modifier = Modifier,
 ) {
     Column(
         modifier = modifier,
         horizontalAlignment = Alignment.End,
         verticalArrangement = Arrangement.spacedBy(8.dp),
     ) {
         Text(
             text = stringResource(Res.string.time_signature_note_count_header),
             style = MaterialTheme.typography.subtitle1,
         )
 
         val noteCounts = remember { (1..32).toList() }
         WheelPicker(
             selectedIndex = noteCounts
                 .indexOfFirst { it == value }
                 .coerceIn(noteCounts.indices),
             items = noteCounts,
             onItemSelect = { _, item -> onValueChange(item) },
             modifier = Modifier.fillMaxSize(),
         ) { index, item, isSelected, closenessToSelection ->
             WheelItem(
                 item = item.toString(),
                 isSelected = isSelected,
                 closenessToSelection = closenessToSelection,
                 textAlign = TextAlign.End,
             )
         }
     }
 }
 
 @Composable
 private fun NoteValueColumn(
     value: Int,
     onValueChange: (Int) -> Unit,
     modifier: Modifier = Modifier,
 ) {
     Column(
         modifier = modifier,
         horizontalAlignment = Alignment.Start,
         verticalArrangement = Arrangement.spacedBy(8.dp),
     ) {
         Text(
             text = stringResource(Res.string.time_signature_note_value_header),
             style = MaterialTheme.typography.subtitle1,
         )
 
         val possibleNoteValues = remember {
             listOf(
                 1 to Res.string.time_signature_note_value_whole,
                 2 to Res.string.time_signature_note_value_half,
                 4 to Res.string.time_signature_note_value_quarter,
                 8 to Res.string.time_signature_note_value_eighth,
                 16 to Res.string.time_signature_note_value_sixteenth,
                 32 to Res.string.time_signature_note_value_thirty_second,
             )
         }
 
         WheelPicker(
             selectedIndex = possibleNoteValues
                 .indexOfFirst { it.first == value }
                 .coerceIn(possibleNoteValues.indices),
             items = possibleNoteValues.map { it.second },
             onItemSelect = { index, _ -> onValueChange(possibleNoteValues[index].first) },
             modifier = Modifier.fillMaxSize(),
         ) { index, item, isSelected, closenessToSelection ->
             WheelItem(
                 item = stringResource(item),
                 isSelected = isSelected,
                 closenessToSelection = closenessToSelection,
                 textAlign = TextAlign.Start,
             )
         }
     }
 }
 
 @Composable
 private fun LazyItemScope.WheelItem(
     item: String,
     isSelected: Boolean,
     closenessToSelection: Float,
     textAlign: TextAlign,
 ) {
     val easedClosenessToSelection = EaseOutQuad.transform(closenessToSelection)
     val scale = lerp(0.3f, 1f, easedClosenessToSelection)
     val color by animateColorAsState(if (isSelected) MaterialTheme.colors.primary else LocalContentColor.current)
 
     Text(
         text = item,
         modifier = Modifier
             .fillParentMaxWidth()
             .graphicsLayer {
                 transformOrigin = if (textAlign == TextAlign.Start) {
                     TransformOrigin(0f, 0.5f)
                 } else {
                     TransformOrigin(1f, 0.5f)
                 }
                 scaleX = scale
                 scaleY = scale
             }
             .alpha(easedClosenessToSelection),
         color = color,
         textAlign = textAlign,
         style = MaterialTheme.typography.subtitle1,
     )
 }
 
 @Preview
 @Composable
 private fun Preview() = ClickTrackTheme {
     TimeSignatureEditDialog(
         value = TimeSignature(4, 4),
         onValueChange = {},
         onDismissRequest = {},
     )
 }