diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt index b1122db..ef32286 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/DrawerUI.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight @@ -21,8 +22,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -31,6 +36,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import mg.dot.feufaro.Platform +import mg.dot.feufaro.getPlatform import mg.dot.feufaro.pdf.PrintSettings import mg.dot.feufaro.pdf.defaultPrintSettings import mg.dot.feufaro.pdf.rememberPdfExportAction @@ -95,10 +102,11 @@ fun MainScreenWithDrawer( } } - LaunchedEffect(isSearchActive) { - if (isSearchActive) { - focusRequester.requestFocus() - } + val isFullScreenEnabled by sharedScreenModel.isFullScreen.collectAsState() + val fullscreenController = LocalFullscreenController.current + + LaunchedEffect(isFullScreenEnabled) { + fullscreenController(isFullScreenEnabled) } val fileContent = sharedScreenModel.fileContent.value ?: "" LaunchedEffect(Unit) { @@ -213,563 +221,701 @@ fun MainScreenWithDrawer( } if (showSettingsDialog) { Settings( + sharedScreenModel = sharedScreenModel, onDismissRequest = { showSettingsDialog = false } ) } val favoritePaths by sharedScreenModel.playlistItems.collectAsState() - Scaffold(contentWindowInsets = WindowInsets.safeDrawing, topBar = { - TopAppBar( - modifier = Modifier.height(55.dp).windowInsetsPadding(WindowInsets.safeDrawing), - title = { - Column( - modifier = Modifier.fillMaxSize().verticalScroll(scrollState), - verticalArrangement = Arrangement.Center - ) { - Row { - Text( - songTitle, - modifier = Modifier.weight(1f, fill = true), - maxLines = 1, - softWrap = false, - overflow = TextOverflow.Ellipsis, - ) - } - } - }, navigationIcon = { - IconButton(onClick = { - scope.launch { drawerState.open() } - }) { - Icon(Icons.Filled.Menu, contentDescription = "Ouvrir Menu") - } - }, actions = { - var tempInterval by remember(fileContent) { mutableStateOf(0) } - var isEyeVisible by remember { mutableStateOf(false) } - val keysOrder = Transpose.keyToNumber - val songKeyIndex = keysOrder.indexOf(songKey).takeIf { it != -1 } ?: 0 - val rawKeyIndex = (songKeyIndex + tempInterval) % 12 - val tempUiKey = keysOrder[if (rawKeyIndex < 0) rawKeyIndex + 12 else rawKeyIndex] - val appliedInterval by sharedScreenModel.transpositionInterval.collectAsState() - val isPendingChange = tempInterval != appliedInterval - val isCurrentlyTransposed = appliedInterval != 0 - - Box( - modifier = Modifier.padding(end = 16.dp), - contentAlignment = Alignment.TopEnd - ) { - Row( - modifier = Modifier.fillMaxHeight(), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = tempUiKey, - fontSize = 25.sp, - fontWeight = FontWeight.Black, - textAlign = TextAlign.Center, - color = if (isCurrentlyTransposed && !isPendingChange) Color(0xFFFFD700) else Color.White, - modifier = Modifier - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null - ) { - isEyeVisible = !isEyeVisible - } - .width(45.dp) - ) - } - - DropdownMenu( - expanded = isEyeVisible, - onDismissRequest = { isEyeVisible = false }, - containerColor = Color(0xFF2C3135).copy(alpha = 0.75f), - shape = RoundedCornerShape(16.dp), - modifier = Modifier.padding(12.dp) - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - /* < = > */ - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceEvenly, - modifier = Modifier.padding(horizontal = 4.dp) - ) { - /* < */ - val currentIndex = keysOrder.indexOf(tempUiKey).takeIf { it != -1 } ?: 0 - val targetKeyLeft = keysOrder[(currentIndex - 1 + 12) % 12] - - TooltipBox( - positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), - tooltip = { PlainTooltip(containerColor = Color.DarkGray, contentColor = Color.White) { Text(targetKeyLeft, fontSize = 12.sp) } }, - state = rememberTooltipState() - ) { - IconButton( - modifier = Modifier.size(36.dp), - onClick = { tempInterval-- } - ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.KeyboardArrowLeft, - contentDescription = null, - tint = Color.White - ) - } - } - - val centralIcon = if (isPendingChange) Icons.Filled.Check else Icons.Filled.SwapHoriz - val centralTint = if (isPendingChange) Color(0xFFFFD700) else Color.White - val tooltipText = if (isPendingChange) "Transposer en $tempUiKey" else if (isCurrentlyTransposed) "Revenir en $songKey" else "Transposer" - - TooltipBox( - positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), - tooltip = { - PlainTooltip(containerColor = Color.DarkGray, contentColor = Color.White) { Text(text = tooltipText, fontSize = 12.sp) } - }, - state = rememberTooltipState() - ) { - IconButton( - modifier = Modifier.size(36.dp), - onClick = { - if (!isPendingChange && isCurrentlyTransposed) { - tempInterval = 0 - sharedScreenModel.setTranspositionInterval(0) - } else { - sharedScreenModel.setTranspositionInterval(tempInterval) - } - solfaScreenModel.loadFromFile(sharedScreenModel.activeFilePath.value) - isEyeVisible = false - } - ) { - Icon( - imageVector = centralIcon, - contentDescription = null, - tint = centralTint - ) - } - } - - /* > */ - val targetKeyRight = keysOrder[(currentIndex + 1) % 12] - TooltipBox( - positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), - tooltip = { PlainTooltip(containerColor = Color.DarkGray, contentColor = Color.White) { Text(targetKeyRight, fontSize = 12.sp) } }, - state = rememberTooltipState() - ) { - IconButton( - modifier = Modifier.size(36.dp), - onClick = { tempInterval++ } - ) { - Icon( - imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, - contentDescription = null, - tint = Color.White - ) - } - } - } - - val cIndex = keysOrder.indexOf("C") - val intervalToC = if (cIndex != -1) cIndex - songKeyIndex else 0 - - TooltipBox( - positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), - tooltip = { - PlainTooltip(containerColor = Color.DarkGray, contentColor = Color.White) { Text(text = "Transposer en Do (C)", fontSize = 12.sp) } - }, - state = rememberTooltipState() - ) { - OutlinedButton( - onClick = { - tempInterval = intervalToC - sharedScreenModel.setTranspositionInterval(intervalToC) - solfaScreenModel.loadFromFile(sharedScreenModel.activeFilePath.value) - isEyeVisible = false - }, - modifier = Modifier.fillMaxWidth().height(36.dp), - shape = RoundedCornerShape(8.dp), - border = BorderStroke(1.dp, Color(0xFFFFD700).copy(alpha = 0.5f)), - colors = ButtonDefaults.outlinedButtonColors(contentColor = Color(0xFFFFD700)) - ) { - Text( - text = "Transposer en C", - fontSize = 12.sp, - fontWeight = FontWeight.Bold - ) - } - } - } - } - } - }, colors = TopAppBarColors( - containerColor = MaterialTheme.colorScheme.primary, - titleContentColor = MaterialTheme.colorScheme.onPrimary, - actionIconContentColor = MaterialTheme.colorScheme.onPrimary, - navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, - scrolledContainerColor = MaterialTheme.colorScheme.onPrimary, - ) - ) - }, floatingActionButton = { - Row( - modifier = Modifier.fillMaxWidth() //horizontalAlignment = Alignment.CenterHorizontally - ) { - Column( - modifier = Modifier.fillMaxWidth().padding(5.dp), horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(7.dp) - ) { - if (isEditMode) { - AnimatedVisibility( - visible = true, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } - ) { - FloatingActionButton( - onClick = { - scope.launch { - codeContent = sourceContent - - withContext(Dispatchers.Main) { - solfaScreenModel.loadExternalFile(originalPath) - } - } - }, modifier = Modifier.alpha(0.45f) - ) { - Icon( - imageVector = Icons.AutoMirrored.Default.Undo, - contentDescription = null, - tint = Color.Blue - ) - } - } - - AnimatedVisibility( - visible = true, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } - ) { - FloatingActionButton( - onClick = { - scope.launch { - try { - val initialDir = solfaScreenModel.fileRepository.getAppPublicFolder().absolutePath - saveLauncher.launch(fileName, initialDir) - } catch (e: Exception) { - e.printStackTrace() - } - } - }, modifier = Modifier.alpha(0.45f) - ) { - Icon( - imageVector = Icons.Filled.SaveAs, - contentDescription = null, - tint = Color.Blue - ) - } - } - - AnimatedVisibility( - visible = true, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } - ) { - FloatingActionButton( - onClick = { - sharedScreenModel.toggleEditorMode(false) - }, modifier = Modifier.alpha(0.45f) - ) { - Icon( - imageVector = Icons.Default.Close, - contentDescription = null, - tint = Color.Blue - ) - } - } - } else { - AnimatedVisibility( - visible = isExpanded and !showMidiCtrl, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } - ) { - Row { - Column { - FloatingActionButton( - onClick = { - sharedScreenModel.descGridCount(1) - }, modifier = Modifier.size(30.dp).alpha(0.45f) - ) { - Icon( - Icons.Default.Remove, - contentDescription = null, - tint = Color.Blue - ) - } - } - Column { - FloatingActionButton( - onClick = { - sharedScreenModel.addGridCount(1) - }, modifier = Modifier.size(30.dp).alpha(0.45f) - ) { - Icon( - Icons.Default.Add, - contentDescription = null, - tint = Color.Blue - ) - } - } - } - } - AnimatedVisibility( - visible = isExpanded and !showMidiCtrl, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } - ) { - FloatingActionButton( - onClick = { - sharedScreenModel.toggleQRCodeVisibility() - sharedScreenModel.setExpandedFAB(false) - }, modifier = Modifier.alpha(0.45f) - ) { - Icon( - imageVector = Icons.Filled.QrCode, - contentDescription = "share qr", - tint = Color.Blue - ) - } - } - - - AnimatedVisibility( - visible = isExpanded and !showMidiCtrl, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } - ) { - FloatingActionButton( - onClick = { - showPrintSettings = !showPrintSettings - }, modifier = Modifier.alpha(0.45f) - ) { - Icon( - imageVector = Icons.Filled.Print, - contentDescription = "Imprimer", - tint = Color.Blue - ) - } - } - - AnimatedVisibility( - visible = isExpanded, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } - ) { - FloatingActionButton( - onClick = { - sharedScreenModel.setMidiCtrl(!showMidiCtrl) - }, modifier = Modifier.alpha(0.45f) - ) { - Icon( - imageVector = if(showMidiCtrl) Icons.Filled.StopCircle else Icons.Filled.PlayCircle, - contentDescription = "Jouer", - tint = Color.Blue - ) - } - } - if (!showMidiCtrl) { - FloatingActionButton( - onClick = { - sharedScreenModel.setExpandedFAB(!isExpanded) - refreshTrigeer++ - sharedScreenModel.loadNewSong("$midiFile") - }, modifier = Modifier.alpha(0.45f) - ) { - Icon( - imageVector = if (isExpanded) Icons.Filled.Close else Icons.Filled.Menu, - contentDescription = "MenuFermer", - tint = Color.Blue - ) - } - } - AnimatedVisibility( - visible = showMidiCtrl, - enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, - exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } - ) { - Box( - modifier = Modifier.fillMaxWidth(0.9f) - ) { - if(player != null) { - MidiControlPanel( - isPause = isPos, - currentPos = currentPos, - volume = volumelevel, - duration = duration, - solfaScrollState, - onPlayPauseClick = { - sharedScreenModel.togglePlayPause() - }, - onSeek = { newPos -> - sharedScreenModel.setDragging(true) - sharedScreenModel.seekTo(newPos) - scope.launch { - delay(100) - sharedScreenModel.setDragging(false) - } - //println("DrawerUI:335: mihetsika $newPos") - }, - mediaPlayer = player, - onVolumeChange = { newVolume -> - sharedScreenModel.setVolume(newVolume) -// println("Changement volume $newVolume -l $volumelevel") - }, - onVoiceVolumeChange = { index, volume -> - player?.updateVoiceVolume(index, volume) - } - ) - } else { - Text("Sélectionner un morceau") - } - } - } - } - } - } - }) { paddingValues -> - - Box( - modifier = Modifier.fillMaxSize().padding(paddingValues).consumeWindowInsets(paddingValues).windowInsetsPadding(WindowInsets.safeDrawing.union(WindowInsets.ime)) - ) { - content(PaddingValues(0.dp)) - if (sharedScreenModel.isQRCodeVisible.value) { - QRDisplay( - sharedScreenModel = sharedScreenModel, - fileRepository = solfaScreenModel.fileRepository - ) + BoxWithConstraints(modifier = Modifier.fillMaxSize()) { + val platform = getPlatform() + val isAndroid = platform.name.startsWith("Android") + val topAppBarHeight = if (isAndroid) { + if(maxWidth > maxHeight) { + 55.dp } else { - if (filteredSongs.isNotEmpty()) { - Column( - modifier = Modifier.fillMaxWidth(0.75f).align(Alignment.TopCenter) - .background(MaterialTheme.colorScheme.surface).border( - 1.dp, - MaterialTheme.colorScheme.outlineVariant, - shape = MaterialTheme.shapes.medium - ) - ) { - if (filteredSongs.isNotEmpty()) { - val sortedSongs = remember(filteredSongs) { - filteredSongs.sortedBy { item -> - item.path.startsWith("assets://") - } + if (isFullScreenEnabled) 70.dp else 80.dp + } + } else { + 55.dp + } + + Scaffold( + contentWindowInsets = if (isAndroid && !isFullScreenEnabled) { + WindowInsets.safeDrawing.union(WindowInsets.displayCutout) + } else { + WindowInsets(0, 0, 0, 0) + }, + topBar = { + TopAppBar( + modifier = Modifier.height(topAppBarHeight) + .windowInsetsPadding( + if (isAndroid && !isFullScreenEnabled) { + WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal) + } else { + WindowInsets(0, 0, 0, 0) } - LazyColumn(Modifier.fillMaxSize()) { - itemsIndexed(sortedSongs, key = {_, item -> item.path}) { index, item -> - val isFavorite = favoritePaths.contains(item) - ListItem( - leadingContent = { - Icon( - imageVector = if(item.path.startsWith("assets://")) Icons.Default.ArrowDropDownCircle else Icons.Default.Folder, - contentDescription = null, - modifier = Modifier.size(30.dp) - ) - }, - headlineContent = { Text(item.title) }, - supportingContent = { Text(item.contentTitle, maxLines = 1) }, - trailingContent = { - IconButton( - onClick = { sharedScreenModel.toggleFavorite(item.path) }, - modifier = Modifier.size(25.dp) - ) { - Icon( - imageVector = Icons.Default.Star, - contentDescription = null, - tint = if (!isFavorite) Color.LightGray else Color(0xFFFFD700) - ) - } - }, - modifier = Modifier.clickable { - sharedScreenModel.updateSearchTxt("") - sharedScreenModel.reset() - solfaScreenModel.loadFromFile(item.path) - sharedScreenModel.stopMidi() - sharedScreenModel.seekTo(0f) - sharedScreenModel.setTranspositionInterval(0) - }) - HorizontalDivider() - } - } - } else { - content(paddingValues) - } - } - } - - - Box( - modifier = Modifier.fillMaxSize().fillMaxWidth(0.75f).align(Alignment.Center) - .padding(paddingValues).windowInsetsPadding(WindowInsets.safeDrawing) - ) { - Row( - modifier = Modifier.align(Alignment.TopEnd).padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - Column( - - ) { - - AnimatedContent( - targetState = isSearchActive, label = "Search Transition" - ) { targetIsActive -> - if (targetIsActive) { - TextField( + ), + title = { + AnimatedContent( + targetState = isSearchActive, + label = "SearchTransition" + ) { searchActive -> + if (searchActive) { + Box( + modifier = Modifier.fillMaxHeight(), + contentAlignment = Alignment.Center + ) { + BasicTextField( value = textInput, onValueChange = { newValue -> textInput = newValue sharedScreenModel.updateSearchTxt(newValue) }, - placeholder = { - Text("... ...") - }, - textStyle = MaterialTheme.typography.titleLarge.copy( - fontSize = 17.sp, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onSurface + textStyle = LocalTextStyle.current.copy( + color = Color.White, + fontSize = 16.sp ), - modifier = Modifier.focusRequester(focusRequester).fillMaxWidth(0.45f) - .border( - width = 2.dp, - color = Color.Gray, - shape = RoundedCornerShape(8.dp) + singleLine = true, + cursorBrush = SolidColor(Color.White), + modifier = Modifier + .fillMaxWidth() + .height(38.dp) + .focusRequester(focusRequester) + .background( + color = Color.White.copy(alpha = 0.15f), + shape = RoundedCornerShape(20.dp) + ), + decorationBox = { innerTextField -> + TextFieldDefaults.DecorationBox( + value = textInput, + innerTextField = innerTextField, + enabled = true, + singleLine = true, + visualTransformation = VisualTransformation.None, + interactionSource = remember { MutableInteractionSource() }, + placeholder = { + Text( + text = "FFPM 1 Andriananahary ...", + color = Color.White.copy(alpha = 0.5f), + fontSize = 16.sp + ) + }, + leadingIcon = { + Icon( + imageVector = Icons.Default.Search, + contentDescription = null, + tint = Color.White.copy(alpha = 0.8f), + modifier = Modifier.size(20.dp) + ) + }, + trailingIcon = { + if (textInput.isNotEmpty()) { + IconButton( + onClick = { + textInput = "" + sharedScreenModel.updateSearchTxt("") + }, + modifier = Modifier.size(32.dp) + ) { + Icon( + imageVector = Icons.Default.Clear, + contentDescription = "Effacer", + tint = Color.White.copy(alpha = 0.8f), + modifier = Modifier.size(18.dp) + ) + } + } + }, + contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ) ) - .alpha(0.6f) + } ) } + } else { + Column( + modifier = Modifier.fillMaxSize().verticalScroll(scrollState), + verticalArrangement = Arrangement.Center + ) { + Row { + Text( + songTitle, + modifier = Modifier.weight(1f, fill = true), + maxLines = 1, + softWrap = false, + overflow = TextOverflow.Ellipsis, + ) + } + } } } - if(!isEditMode) { - Column { - IconButton( + }, navigationIcon = { + IconButton(onClick = { + scope.launch { drawerState.open() } + }) { + Icon(Icons.Filled.Menu, contentDescription = "Ouvrir Menu") + } + }, actions = { + var tempInterval by remember(fileContent) { mutableStateOf(0) } + var isEyeVisible by remember { mutableStateOf(false) } + val keysOrder = Transpose.keyToNumber + val songKeyIndex = keysOrder.indexOf(songKey).takeIf { it != -1 } ?: 0 + val rawKeyIndex = (songKeyIndex + tempInterval) % 12 + val tempUiKey = keysOrder[if (rawKeyIndex < 0) rawKeyIndex + 12 else rawKeyIndex] + val appliedInterval by sharedScreenModel.transpositionInterval.collectAsState() + val isPendingChange = tempInterval != appliedInterval + val isCurrentlyTransposed = appliedInterval != 0 + + Box( + modifier = Modifier.padding(end = 16.dp), + contentAlignment = Alignment.TopCenter + ) { + Row( + modifier = Modifier.fillMaxHeight(), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = tempUiKey, + fontSize = 25.sp, + fontWeight = FontWeight.Black, + textAlign = TextAlign.Center, + color = if (isCurrentlyTransposed && !isPendingChange) Color(0xFFFFD700) else Color.White, + modifier = Modifier + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null + ) { + isEyeVisible = !isEyeVisible + } + .width(45.dp) + ) + } + + DropdownMenu( + expanded = isEyeVisible, + onDismissRequest = { isEyeVisible = false }, + containerColor = Color(0xFF2C3135).copy(alpha = 0.75f), + shape = RoundedCornerShape(16.dp), + modifier = Modifier.padding(12.dp) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + /* < = > */ + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier.padding(horizontal = 4.dp) + ) { + /* < */ + val currentIndex = keysOrder.indexOf(tempUiKey).takeIf { it != -1 } ?: 0 + val targetKeyLeft = keysOrder[(currentIndex - 1 + 12) % 12] + + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip( + containerColor = Color.DarkGray, + contentColor = Color.White + ) { Text(targetKeyLeft, fontSize = 12.sp) } + }, + state = rememberTooltipState() + ) { + IconButton( + modifier = Modifier.size(36.dp), + onClick = { tempInterval-- } + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.KeyboardArrowLeft, + contentDescription = null, + tint = Color.White + ) + } + } + + val centralIcon = if (isPendingChange) Icons.Filled.Check else Icons.Filled.SwapHoriz + val centralTint = if (isPendingChange) Color(0xFFFFD700) else Color.White + val tooltipText = if (isPendingChange) "Transposer en $tempUiKey" else if (isCurrentlyTransposed) "Revenir en $songKey" else "Transposer" + + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip( + containerColor = Color.DarkGray, + contentColor = Color.White + ) { Text(text = tooltipText, fontSize = 12.sp) } + }, + state = rememberTooltipState() + ) { + IconButton( + modifier = Modifier.size(36.dp), + onClick = { + if (!isPendingChange && isCurrentlyTransposed) { + tempInterval = 0 + sharedScreenModel.setTranspositionInterval(0) + } else { + sharedScreenModel.setTranspositionInterval(tempInterval) + } + solfaScreenModel.loadFromFile(sharedScreenModel.activeFilePath.value) + isEyeVisible = false + } + ) { + Icon( + imageVector = centralIcon, + contentDescription = null, + tint = centralTint + ) + } + } + + /* > */ + val targetKeyRight = keysOrder[(currentIndex + 1) % 12] + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip( + containerColor = Color.DarkGray, + contentColor = Color.White + ) { Text(targetKeyRight, fontSize = 12.sp) } + }, + state = rememberTooltipState() + ) { + IconButton( + modifier = Modifier.size(36.dp), + onClick = { tempInterval++ } + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, + contentDescription = null, + tint = Color.White + ) + } + } + } + + val cIndex = keysOrder.indexOf("C") + val intervalToC = if (cIndex != -1) cIndex - songKeyIndex else 0 + + TooltipBox( + positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(), + tooltip = { + PlainTooltip( + containerColor = Color.DarkGray, + contentColor = Color.White + ) { Text(text = "Transposer en Do (C)", fontSize = 12.sp) } + }, + state = rememberTooltipState() + ) { + OutlinedButton( + onClick = { + tempInterval = intervalToC + sharedScreenModel.setTranspositionInterval(intervalToC) + solfaScreenModel.loadFromFile(sharedScreenModel.activeFilePath.value) + isEyeVisible = false + }, + modifier = Modifier.fillMaxWidth().height(36.dp), + shape = RoundedCornerShape(8.dp), + border = BorderStroke(1.dp, Color(0xFFFFD700).copy(alpha = 0.5f)), + colors = ButtonDefaults.outlinedButtonColors( + contentColor = Color( + 0xFFFFD700 + ) + ) + ) { + Text( + text = "Transposer en C", + fontSize = 12.sp, + fontWeight = FontWeight.Bold + ) + } + } + } + } + } + }, colors = TopAppBarColors( + containerColor = MaterialTheme.colorScheme.primary, + titleContentColor = MaterialTheme.colorScheme.onPrimary, + actionIconContentColor = MaterialTheme.colorScheme.onPrimary, + navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, + scrolledContainerColor = MaterialTheme.colorScheme.onPrimary, + ) + ) + }, floatingActionButton = { + Row( + modifier = Modifier.fillMaxWidth() + ) { + Column( + modifier = Modifier.fillMaxWidth().padding(5.dp), horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.spacedBy(7.dp) + ) { + if (isEditMode) { + AnimatedVisibility( + visible = true, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + FloatingActionButton( onClick = { - sharedScreenModel.showSearchMenu(!isSearchActive) - sharedScreenModel.updateSearchTxt("") - textInput = "" - }, modifier = Modifier.size(56.dp).alpha(0.45f).background( - color = Color.Blue, shape = CircleShape - ) + scope.launch { + codeContent = sourceContent + + withContext(Dispatchers.Main) { + solfaScreenModel.loadExternalFile(originalPath) + } + } + }, modifier = Modifier.alpha(0.45f) ) { Icon( - if(isSearchActive) Icons.Default.Close else Icons.Default.Search, - contentDescription = "la recherche", - tint = Color.White + imageVector = Icons.AutoMirrored.Default.Undo, + contentDescription = null, + tint = Color.Blue ) } } + + AnimatedVisibility( + visible = true, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + FloatingActionButton( + onClick = { + scope.launch { + try { + val initialDir = + solfaScreenModel.fileRepository.getAppPublicFolder().absolutePath + saveLauncher.launch(fileName, initialDir) + } catch (e: Exception) { + e.printStackTrace() + } + } + }, modifier = Modifier.alpha(0.45f) + ) { + Icon( + imageVector = Icons.Filled.SaveAs, + contentDescription = null, + tint = Color.Blue + ) + } + } + + AnimatedVisibility( + visible = true, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + FloatingActionButton( + onClick = { + sharedScreenModel.toggleEditorMode(false) + }, modifier = Modifier.alpha(0.45f) + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = null, + tint = Color.Blue + ) + } + } + } else { + AnimatedVisibility( + visible = isExpanded and !showMidiCtrl, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + Row { + Column { + FloatingActionButton( + onClick = { + sharedScreenModel.descGridCount(1) + }, modifier = Modifier.size(30.dp).alpha(0.45f) + ) { + Icon( + Icons.Default.Remove, + contentDescription = null, + tint = Color.Blue + ) + } + } + Column { + FloatingActionButton( + onClick = { + sharedScreenModel.addGridCount(1) + }, modifier = Modifier.size(30.dp).alpha(0.45f) + ) { + Icon( + Icons.Default.Add, + contentDescription = null, + tint = Color.Blue + ) + } + } + } + } + AnimatedVisibility( + visible = isExpanded and !showMidiCtrl, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + FloatingActionButton( + onClick = { + sharedScreenModel.toggleQRCodeVisibility() + sharedScreenModel.setExpandedFAB(false) + }, modifier = Modifier.alpha(0.45f) + ) { + Icon( + imageVector = Icons.Filled.QrCode, + contentDescription = "share qr", + tint = Color.Blue + ) + } + } + + + AnimatedVisibility( + visible = isExpanded and !showMidiCtrl, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + FloatingActionButton( + onClick = { + showPrintSettings = !showPrintSettings + }, modifier = Modifier.alpha(0.45f) + ) { + Icon( + imageVector = Icons.Filled.Print, + contentDescription = "Imprimer", + tint = Color.Blue + ) + } + } + + AnimatedVisibility( + visible = isExpanded, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + FloatingActionButton( + onClick = { + sharedScreenModel.setMidiCtrl(!showMidiCtrl) + }, modifier = Modifier.alpha(0.45f) + ) { + Icon( + imageVector = if (showMidiCtrl) Icons.Filled.StopCircle else Icons.Filled.PlayCircle, + contentDescription = "Jouer", + tint = Color.Blue + ) + } + } + if (!showMidiCtrl) { + FloatingActionButton( + onClick = { + sharedScreenModel.setExpandedFAB(!isExpanded) + refreshTrigeer++ + sharedScreenModel.loadNewSong("$midiFile") + }, modifier = Modifier.alpha(0.45f) + ) { + Icon( + imageVector = if (isExpanded) Icons.Filled.Close else Icons.Filled.Menu, + contentDescription = "MenuFermer", + tint = Color.Blue + ) + } + } + AnimatedVisibility( + visible = showMidiCtrl, + enter = fadeIn() + scaleIn() + slideInVertically { it / 2 }, + exit = fadeOut() + scaleOut() + slideOutVertically { it / 2 } + ) { + Box( + modifier = Modifier.fillMaxWidth(0.9f) + ) { + if (player != null) { + MidiControlPanel( + isPause = isPos, + currentPos = currentPos, + volume = volumelevel, + duration = duration, + solfaScrollState, + onPlayPauseClick = { + sharedScreenModel.togglePlayPause() + }, + onSeek = { newPos -> + sharedScreenModel.setDragging(true) + sharedScreenModel.seekTo(newPos) + scope.launch { + delay(100) + sharedScreenModel.setDragging(false) + } + //println("DrawerUI:335: mihetsika $newPos") + }, + mediaPlayer = player, + onVolumeChange = { newVolume -> + sharedScreenModel.setVolume(newVolume) + // println("Changement volume $newVolume -l $volumelevel") + }, + onVoiceVolumeChange = { index, volume -> + player?.updateVoiceVolume(index, volume) + } + ) + } else { + Text("Sélectionner un morceau") + } + } + } } } } + }) { paddingValues -> + Box( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .consumeWindowInsets(paddingValues) + /*.windowInsetsPadding(currentInsets.union(WindowInsets.ime))*/ + ) { + content(PaddingValues(0.dp)) + if (sharedScreenModel.isQRCodeVisible.value) { + QRDisplay( + sharedScreenModel = sharedScreenModel, + fileRepository = solfaScreenModel.fileRepository + ) + } + AnimatedVisibility( + visible = !isEditMode, + modifier = Modifier + .align(Alignment.TopEnd) + .padding(16.dp) + ) { + IconButton( + onClick = { + if (isSearchActive) { + sharedScreenModel.showSearchMenu(false) + sharedScreenModel.updateSearchTxt("") + textInput = "" + } else { + sharedScreenModel.showSearchMenu(true) + scope.launch { + delay(100) + focusRequester.requestFocus() + } + } + }, + modifier = Modifier + .size(56.dp) + .alpha(0.6f) + .background( + color = Color.Blue, + shape = CircleShape + ) + ) { + Icon( + imageVector = if (isSearchActive) Icons.Default.Close else Icons.Default.Search, + contentDescription = null, + tint = Color.White + ) + } + } + AnimatedVisibility( + visible = isSearchActive && textInput.isNotEmpty(), + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + modifier = Modifier + .align(Alignment.TopCenter) + .fillMaxWidth(0.85f) + .padding(top = 8.dp) + ) { + Card( + elevation = CardDefaults.cardElevation(defaultElevation = 8.dp), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), + ) { + val sortedSongs = remember(filteredSongs) { + filteredSongs.sortedBy { item -> + item.path.startsWith("assets://") + } + } + LazyColumn(Modifier.fillMaxSize()) { + if (sortedSongs.isEmpty() && textInput.isNotEmpty()) { + item { + ListItem( + headlineContent = { + Text( + text = "Partition non trouvée pour \"$textInput\"", + color = MaterialTheme.colorScheme.error, + fontWeight = FontWeight.Medium, + fontSize = 14.sp + ) + }, + supportingContent = { + Text( + text = "Essayez un autre.", + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + }, + leadingContent = { + Icon( + imageVector = Icons.Default.SearchOff, + contentDescription = null, + tint = MaterialTheme.colorScheme.error + ) + } + ) + } + } + itemsIndexed(sortedSongs, key = { _, item -> item.path }) { index, item -> + val isFavorite = favoritePaths.contains(item) + ListItem( + leadingContent = { + Icon( + imageVector = if (item.path.startsWith("assets://")) Icons.Default.ArrowDropDownCircle else Icons.Default.Folder, + contentDescription = null, + modifier = Modifier.size(30.dp) + ) + }, + headlineContent = { Text(item.title) }, + supportingContent = { Text(item.contentTitle, maxLines = 1) }, + trailingContent = { + IconButton( + onClick = { sharedScreenModel.toggleFavorite(item.path) }, + modifier = Modifier.size(25.dp) + ) { + Icon( + imageVector = Icons.Default.Star, + contentDescription = null, + tint = if (!isFavorite) Color.LightGray else Color(0xFFFFD700) + ) + } + }, + modifier = Modifier.clickable { + sharedScreenModel.updateSearchTxt("") + sharedScreenModel.reset() + solfaScreenModel.loadFromFile(item.path) + sharedScreenModel.stopMidi() + sharedScreenModel.seekTo(0f) + sharedScreenModel.setTranspositionInterval(0) + }) + HorizontalDivider() + } + } + } + } } } } }) +} + +val LocalFullscreenController = staticCompositionLocalOf<(Boolean) -> Unit> { + { _ -> } } \ No newline at end of file