Move search to TopAppBar & Android fullscreen toggle in Settings

This commit is contained in:
Hasinjato 2026-06-17 10:54:43 +03:00
parent b4e9578d5e
commit da1f5d8fbb

View file

@ -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,16 +221,125 @@ fun MainScreenWithDrawer(
}
if (showSettingsDialog) {
Settings(
sharedScreenModel = sharedScreenModel,
onDismissRequest = {
showSettingsDialog = false
}
)
}
val favoritePaths by sharedScreenModel.playlistItems.collectAsState()
Scaffold(contentWindowInsets = WindowInsets.safeDrawing, topBar = {
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
val platform = getPlatform()
val isAndroid = platform.name.startsWith("Android")
val topAppBarHeight = if (isAndroid) {
if(maxWidth > maxHeight) {
55.dp
} else {
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(55.dp).windowInsetsPadding(WindowInsets.safeDrawing),
modifier = Modifier.height(topAppBarHeight)
.windowInsetsPadding(
if (isAndroid && !isFullScreenEnabled) {
WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)
} else {
WindowInsets(0, 0, 0, 0)
}
),
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)
},
textStyle = LocalTextStyle.current.copy(
color = Color.White,
fontSize = 16.sp
),
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
)
)
}
)
}
} else {
Column(
modifier = Modifier.fillMaxSize().verticalScroll(scrollState),
verticalArrangement = Arrangement.Center
@ -237,6 +354,8 @@ fun MainScreenWithDrawer(
)
}
}
}
}
}, navigationIcon = {
IconButton(onClick = {
scope.launch { drawerState.open() }
@ -256,7 +375,7 @@ fun MainScreenWithDrawer(
Box(
modifier = Modifier.padding(end = 16.dp),
contentAlignment = Alignment.TopEnd
contentAlignment = Alignment.TopCenter
) {
Row(
modifier = Modifier.fillMaxHeight(),
@ -302,7 +421,12 @@ fun MainScreenWithDrawer(
TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
tooltip = { PlainTooltip(containerColor = Color.DarkGray, contentColor = Color.White) { Text(targetKeyLeft, fontSize = 12.sp) } },
tooltip = {
PlainTooltip(
containerColor = Color.DarkGray,
contentColor = Color.White
) { Text(targetKeyLeft, fontSize = 12.sp) }
},
state = rememberTooltipState()
) {
IconButton(
@ -324,7 +448,10 @@ fun MainScreenWithDrawer(
TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
tooltip = {
PlainTooltip(containerColor = Color.DarkGray, contentColor = Color.White) { Text(text = tooltipText, fontSize = 12.sp) }
PlainTooltip(
containerColor = Color.DarkGray,
contentColor = Color.White
) { Text(text = tooltipText, fontSize = 12.sp) }
},
state = rememberTooltipState()
) {
@ -353,7 +480,12 @@ fun MainScreenWithDrawer(
val targetKeyRight = keysOrder[(currentIndex + 1) % 12]
TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
tooltip = { PlainTooltip(containerColor = Color.DarkGray, contentColor = Color.White) { Text(targetKeyRight, fontSize = 12.sp) } },
tooltip = {
PlainTooltip(
containerColor = Color.DarkGray,
contentColor = Color.White
) { Text(targetKeyRight, fontSize = 12.sp) }
},
state = rememberTooltipState()
) {
IconButton(
@ -375,7 +507,10 @@ fun MainScreenWithDrawer(
TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
tooltip = {
PlainTooltip(containerColor = Color.DarkGray, contentColor = Color.White) { Text(text = "Transposer en Do (C)", fontSize = 12.sp) }
PlainTooltip(
containerColor = Color.DarkGray,
contentColor = Color.White
) { Text(text = "Transposer en Do (C)", fontSize = 12.sp) }
},
state = rememberTooltipState()
) {
@ -389,7 +524,11 @@ fun MainScreenWithDrawer(
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))
colors = ButtonDefaults.outlinedButtonColors(
contentColor = Color(
0xFFFFD700
)
)
) {
Text(
text = "Transposer en C",
@ -411,7 +550,7 @@ fun MainScreenWithDrawer(
)
}, floatingActionButton = {
Row(
modifier = Modifier.fillMaxWidth() //horizontalAlignment = Alignment.CenterHorizontally
modifier = Modifier.fillMaxWidth()
) {
Column(
modifier = Modifier.fillMaxWidth().padding(5.dp), horizontalAlignment = Alignment.End,
@ -451,7 +590,8 @@ fun MainScreenWithDrawer(
onClick = {
scope.launch {
try {
val initialDir = solfaScreenModel.fileRepository.getAppPublicFolder().absolutePath
val initialDir =
solfaScreenModel.fileRepository.getAppPublicFolder().absolutePath
saveLauncher.launch(fileName, initialDir)
} catch (e: Exception) {
e.printStackTrace()
@ -636,7 +776,11 @@ fun MainScreenWithDrawer(
}) { paddingValues ->
Box(
modifier = Modifier.fillMaxSize().padding(paddingValues).consumeWindowInsets(paddingValues).windowInsetsPadding(WindowInsets.safeDrawing.union(WindowInsets.ime))
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.consumeWindowInsets(paddingValues)
/*.windowInsetsPadding(currentInsets.union(WindowInsets.ime))*/
) {
content(PaddingValues(0.dp))
if (sharedScreenModel.isQRCodeVisible.value) {
@ -644,23 +788,91 @@ fun MainScreenWithDrawer(
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 {
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
sharedScreenModel.showSearchMenu(true)
scope.launch {
delay(100)
focusRequester.requestFocus()
}
}
},
modifier = Modifier
.size(56.dp)
.alpha(0.6f)
.background(
color = Color.Blue,
shape = CircleShape
)
) {
if (filteredSongs.isNotEmpty()) {
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(
@ -696,80 +908,14 @@ fun MainScreenWithDrawer(
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(
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
),
modifier = Modifier.focusRequester(focusRequester).fillMaxWidth(0.45f)
.border(
width = 2.dp,
color = Color.Gray,
shape = RoundedCornerShape(8.dp)
)
.alpha(0.6f)
)
}
}
}
if(!isEditMode) {
Column {
IconButton(
onClick = {
sharedScreenModel.showSearchMenu(!isSearchActive)
sharedScreenModel.updateSearchTxt("")
textInput = ""
}, modifier = Modifier.size(56.dp).alpha(0.45f).background(
color = Color.Blue, shape = CircleShape
)
) {
Icon(
if(isSearchActive) Icons.Default.Close else Icons.Default.Search,
contentDescription = "la recherche",
tint = Color.White
)
}
}
}
}
}
}
}
}
})
}
val LocalFullscreenController = staticCompositionLocalOf<(Boolean) -> Unit> {
{ _ -> }
}