Improve ui/ux and dynamic theme & fontsize controls on Settings
This commit is contained in:
parent
e91bc7d08e
commit
4070315b94
15 changed files with 942 additions and 421 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"themeMode": "DARK",
|
"themeMode": "LIGHT",
|
||||||
"fontSize": 18.5,
|
"fontSize": 14,
|
||||||
"playlist": [
|
"playlist": [
|
||||||
"assets://a.txt",
|
"assets://a.txt",
|
||||||
"assets://ffpm-617.txt",
|
"assets://ffpm-617.txt",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import SharedScreenModel
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
@ -14,33 +15,26 @@ import kotlinx.coroutines.launch
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
import cafe.adriel.voyager.navigator.CurrentScreen
|
import cafe.adriel.voyager.navigator.CurrentScreen
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
import cafe.adriel.voyager.navigator.Navigator
|
||||||
|
import mg.dot.feufaro.ui.FeufaroTheme
|
||||||
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
import mg.dot.feufaro.viewmodel.SolfaScreenModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun App() {
|
fun App() {
|
||||||
|
|
||||||
|
val displayConfigManager = koinInject<DisplayConfigManager>()
|
||||||
|
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
||||||
|
|
||||||
|
FeufaroTheme(themeModeSelected = currentDisplayConfig.themeMode, userFontSize = currentDisplayConfig.fontSize) {
|
||||||
Navigator(screen = ScreenSolfa) {
|
Navigator(screen = ScreenSolfa) {
|
||||||
CurrentScreen()
|
CurrentScreen()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val fileRepository = koinInject<FileRepository>()
|
val fileRepository = koinInject<FileRepository>()
|
||||||
|
|
||||||
val displayConfigManager = koinInject<DisplayConfigManager>()
|
|
||||||
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
|
||||||
// Load Configurations
|
|
||||||
val configScope = CoroutineScope(Dispatchers.Default)
|
val configScope = CoroutineScope(Dispatchers.Default)
|
||||||
val sharedScreenModel = koinInject<SharedScreenModel>()
|
val sharedScreenModel = koinInject<SharedScreenModel>()
|
||||||
val solfaScreenModel = koinInject<SolfaScreenModel>()
|
val solfaScreenModel = koinInject<SolfaScreenModel>()
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
configScope.launch {
|
|
||||||
try {
|
|
||||||
displayConfigManager.loadConfigFromFile("assets://config.json")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(currentDisplayConfig) {
|
LaunchedEffect(currentDisplayConfig) {
|
||||||
if (currentDisplayConfig.playlist.isNotEmpty()) {
|
if (currentDisplayConfig.playlist.isNotEmpty()) {
|
||||||
if (DeepLinkHandler.hasConsumedDeepLink) {
|
if (DeepLinkHandler.hasConsumedDeepLink) {
|
||||||
|
|
@ -94,10 +88,10 @@ fun MGButton(
|
||||||
val displayConfigManager = koinInject<DisplayConfigManager>()
|
val displayConfigManager = koinInject<DisplayConfigManager>()
|
||||||
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
||||||
val buttonColors = ButtonDefaults.buttonColors(
|
val buttonColors = ButtonDefaults.buttonColors(
|
||||||
containerColor = currentDisplayConfig.buttonContainerColorHex.toColor(),
|
containerColor = MaterialTheme.colorScheme.error,
|
||||||
disabledContainerColor = currentDisplayConfig.buttonDisabledContainerColorHex.toColor(),
|
contentColor = MaterialTheme.colorScheme.onError,
|
||||||
contentColor = currentDisplayConfig.buttonDisabledContentColorHex.toColor(),
|
disabledContainerColor = MaterialTheme.colorScheme.error.copy(alpha = 0.6f),
|
||||||
disabledContentColor = currentDisplayConfig.buttonDisabledContentColorHex.toColor()
|
disabledContentColor = MaterialTheme.colorScheme.onError.copy(alpha = 0.7f)
|
||||||
)
|
)
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
package mg.dot.feufaro
|
package mg.dot.feufaro
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import mg.dot.feufaro.config.AppConfigJson
|
import mg.dot.feufaro.config.AppConfigJson
|
||||||
import mg.dot.feufaro.config.DisplayConfig
|
import mg.dot.feufaro.config.DisplayConfig
|
||||||
import kotlinx.serialization.json.Json
|
import java.util.prefs.Preferences
|
||||||
import kotlin.String
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AppConfigJson(
|
data class AppConfigJson(
|
||||||
|
|
@ -36,20 +38,29 @@ class DisplayConfigManager(
|
||||||
private val fileRepository: FileRepository
|
private val fileRepository: FileRepository
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
private val _displayConfig = MutableStateFlow(
|
private val prefs = Preferences.userRoot().node("mg.dot.feufaro")
|
||||||
DisplayConfig(
|
private val _displayConfig = MutableStateFlow(DisplayConfig())
|
||||||
themeMode = "SYSTEM",
|
|
||||||
fontSize = 16f,
|
|
||||||
playlist = listOf(),
|
|
||||||
buttonContainerColorHex = "#FF000000",
|
|
||||||
buttonContentColorHex = "#FFFFFFFF",
|
|
||||||
buttonDisabledContainerColorHex = "#FF888888",
|
|
||||||
buttonDisabledContentColorHex = "#FFAA6666"
|
|
||||||
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val displayConfig: StateFlow<DisplayConfig> = _displayConfig.asStateFlow()
|
val displayConfig: StateFlow<DisplayConfig> = _displayConfig.asStateFlow()
|
||||||
|
private val configScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadInitialConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadInitialConfig() {
|
||||||
|
configScope.launch {
|
||||||
|
loadConfigFromFile("assets://config.json")
|
||||||
|
|
||||||
|
val jsonConfig = _displayConfig.value
|
||||||
|
val finalTheme = prefs.get("themeMode", jsonConfig.themeMode)
|
||||||
|
val finalFontSize = prefs.getFloat("fontSize", jsonConfig.fontSize)
|
||||||
|
|
||||||
|
_displayConfig.value = jsonConfig.copy(
|
||||||
|
themeMode = finalTheme,
|
||||||
|
fontSize = finalFontSize
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
suspend fun loadConfigFromFile(filePath: String) {
|
suspend fun loadConfigFromFile(filePath: String) {
|
||||||
try {
|
try {
|
||||||
val jsonString = fileRepository.readFileContent(filePath)
|
val jsonString = fileRepository.readFileContent(filePath)
|
||||||
|
|
@ -61,5 +72,29 @@ class DisplayConfigManager(
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateThemeMode(themeMode: String) {
|
||||||
|
prefs.put("themeMode", themeMode)
|
||||||
|
prefs.flush()
|
||||||
|
_displayConfig.value = _displayConfig.value.copy(themeMode = themeMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateFontSize(fontSize: Float) {
|
||||||
|
prefs.putFloat("fontSize", fontSize)
|
||||||
|
prefs.flush()
|
||||||
|
_displayConfig.value = _displayConfig.value.copy(fontSize = fontSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetToDefault() {
|
||||||
|
try {
|
||||||
|
prefs.remove("themeMode")
|
||||||
|
prefs.remove("fontSize")
|
||||||
|
prefs.flush()
|
||||||
|
|
||||||
|
loadInitialConfig()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("Erreur lors de la réinitialisation : ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
// NOUVELLE MÉTHODE : Pour basculer le mode débogage
|
// NOUVELLE MÉTHODE : Pour basculer le mode débogage
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconToggleButton
|
import androidx.compose.material3.IconToggleButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
|
@ -151,14 +152,15 @@ object ScreenSolfa : Screen {
|
||||||
val measureString: String by sharedScreenModel.measure.collectAsState()
|
val measureString: String by sharedScreenModel.measure.collectAsState()
|
||||||
val songTitle: String by sharedScreenModel.songTitle.collectAsState()
|
val songTitle: String by sharedScreenModel.songTitle.collectAsState()
|
||||||
val songKey: String by sharedScreenModel.songKey.collectAsState()
|
val songKey: String by sharedScreenModel.songKey.collectAsState()
|
||||||
Text(text = songTitle, fontWeight = FontWeight.Bold)
|
Text(text = songTitle, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold)
|
||||||
Text(
|
Text(
|
||||||
text = songKey,
|
text = songKey,
|
||||||
modifier = Modifier.background(Color(0xff, 0xea, 0xe7))
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
modifier = Modifier.background(MaterialTheme.colorScheme.tertiary.copy(alpha = 0.1f))
|
||||||
.padding(horizontal = 4.dp)
|
.padding(horizontal = 4.dp)
|
||||||
)
|
)
|
||||||
Text(text = measureString)
|
Text(text = measureString, style = MaterialTheme.typography.bodyLarge)
|
||||||
Text(text = "Stanza: $stanza")
|
Text(text = "Stanza: $stanza", style = MaterialTheme.typography.bodyLarge)
|
||||||
//ScreenTranspose.Content()
|
//ScreenTranspose.Content()
|
||||||
}
|
}
|
||||||
LazyVerticalGridTUO(
|
LazyVerticalGridTUO(
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class DisplayConfig (
|
data class DisplayConfig (
|
||||||
val themeMode: String,
|
val themeMode: String = "LIGHT",
|
||||||
val fontSize: Float,
|
val fontSize: Float = 14f,
|
||||||
val playlist: List<String>,
|
val playlist: List<String> = emptyList(),
|
||||||
val buttonContainerColorHex: String,
|
val buttonContainerColorHex: String = "#737EFC",
|
||||||
val buttonContentColorHex: String ,
|
val buttonContentColorHex: String = "#FFFFFF",
|
||||||
val buttonDisabledContainerColorHex: String,
|
val buttonDisabledContainerColorHex: String = "#CCCCCC",
|
||||||
val buttonDisabledContentColorHex: String ,
|
val buttonDisabledContentColorHex: String = "#888888"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
@ -91,10 +91,16 @@ fun TUODetailDialog(
|
||||||
if (existing.isEmpty() && canAdd) add("_") else add(existing)
|
if (existing.isEmpty() && canAdd) add("_") else add(existing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val lyricsLines = result
|
val lyricsLines = remember(editState.lyricsByStanza) {
|
||||||
.flatMap { it.split("\n") }
|
mutableStateListOf<String>().apply {
|
||||||
.map { it.trim() }
|
addAll(editState.lyricsByStanza.values.toList())
|
||||||
.toMutableList()
|
}
|
||||||
|
}
|
||||||
|
val editedLyricsMap = remember {
|
||||||
|
mutableStateMapOf<Int, String>().apply {
|
||||||
|
putAll(editState.lyricsByStanza)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var canAddMark = mutableStateOf(false)
|
var canAddMark = mutableStateOf(false)
|
||||||
Popup(
|
Popup(
|
||||||
|
|
@ -111,7 +117,7 @@ fun TUODetailDialog(
|
||||||
.widthIn(max=125.dp)
|
.widthIn(max=125.dp)
|
||||||
.heightIn(max = 400.dp),
|
.heightIn(max = 400.dp),
|
||||||
shape = MaterialTheme.shapes.small,
|
shape = MaterialTheme.shapes.small,
|
||||||
color = Color(0xFF2D2D2D).copy(0.75f),
|
color = MaterialTheme.colorScheme.onSecondaryContainer.copy(0.75f),
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(horizontal = 10.dp, vertical = 5.dp).fillMaxWidth()) {
|
Column(modifier = Modifier.padding(horizontal = 10.dp, vertical = 5.dp).fillMaxWidth()) {
|
||||||
Row (
|
Row (
|
||||||
|
|
@ -269,7 +275,7 @@ fun TUODetailDialog(
|
||||||
}) {
|
}) {
|
||||||
Text(
|
Text(
|
||||||
text = "/",
|
text = "/",
|
||||||
color = Color.Cyan,
|
color = MaterialTheme.colorScheme.secondary.copy(alpha = 1.5f),
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
textAlign = TextAlign.Center
|
textAlign = TextAlign.Center
|
||||||
|
|
@ -283,7 +289,7 @@ fun TUODetailDialog(
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Add,
|
imageVector = Icons.Default.Add,
|
||||||
tint = Color.Cyan,
|
tint = MaterialTheme.colorScheme.secondary.copy(alpha = 1.5f),
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +303,7 @@ fun TUODetailDialog(
|
||||||
MyTextEditField(
|
MyTextEditField(
|
||||||
value = templateFragment,
|
value = templateFragment,
|
||||||
customFontSize = 14.sp,
|
customFontSize = 14.sp,
|
||||||
color = Color.Yellow,
|
color = MaterialTheme.colorScheme.tertiary.copy(alpha = 1.5f),
|
||||||
customPadding = 8.dp,
|
customPadding = 8.dp,
|
||||||
customBrush = SolidColor(Color.White),
|
customBrush = SolidColor(Color.White),
|
||||||
isEditable = false,
|
isEditable = false,
|
||||||
|
|
@ -313,7 +319,7 @@ fun TUODetailDialog(
|
||||||
MyTextEditField(
|
MyTextEditField(
|
||||||
value = marker,
|
value = marker,
|
||||||
customFontSize = 14.sp,
|
customFontSize = 14.sp,
|
||||||
color = Color.Yellow,
|
color = MaterialTheme.colorScheme.tertiary.copy(alpha = 1.5f),
|
||||||
customPadding = 8.dp,
|
customPadding = 8.dp,
|
||||||
customBrush = SolidColor(Color.White),
|
customBrush = SolidColor(Color.White),
|
||||||
isEditable = isEditable,
|
isEditable = isEditable,
|
||||||
|
|
@ -342,7 +348,7 @@ fun TUODetailDialog(
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Add,
|
imageVector = Icons.Default.Add,
|
||||||
tint = Color.Green,
|
tint = MaterialTheme.colorScheme.secondary.copy(alpha = 1.5f),
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -366,7 +372,7 @@ fun TUODetailDialog(
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 12.dp),
|
.padding(bottom = 12.dp),
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
color = if (firstValidation.isValid) Color(0xFF10B981) else Color(0xFFF59E0B),
|
color = if (firstValidation.isValid) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.tertiary.copy(alpha = 1.5f),
|
||||||
shadowElevation = 2.dp
|
shadowElevation = 2.dp
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -414,6 +420,14 @@ fun TUODetailDialog(
|
||||||
|
|
||||||
// --- SECTION LYRICS ---
|
// --- SECTION LYRICS ---
|
||||||
lyricsLines.forEachIndexed { index, line ->
|
lyricsLines.forEachIndexed { index, line ->
|
||||||
|
val displayedLine = line
|
||||||
|
LaunchedEffect(displayedLine) {
|
||||||
|
if (displayedLine != line) {
|
||||||
|
lyricsLines[index] = displayedLine
|
||||||
|
val updatedLyricsMap = editState.lyricsByStanza.toMutableMap()
|
||||||
|
updatedLyricsMap[index + 1] = displayedLine
|
||||||
|
}
|
||||||
|
}
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(vertical = 2.dp)
|
modifier = Modifier.padding(vertical = 2.dp)
|
||||||
|
|
@ -421,19 +435,48 @@ fun TUODetailDialog(
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(0.8f)
|
modifier = Modifier.fillMaxWidth(0.8f)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val isLyricsValid = validateLyricsInput(line, templateFragment)
|
||||||
|
val tooltipState2 = rememberTooltipState(isPersistent = false)
|
||||||
|
Row(
|
||||||
|
) {
|
||||||
|
TooltipBox(
|
||||||
|
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
tooltip = {
|
||||||
|
if (!isLyricsValid) {
|
||||||
|
PlainTooltip(
|
||||||
|
containerColor = Color(0XFF34EB71),
|
||||||
|
contentColor = Color.White
|
||||||
|
) {
|
||||||
|
Text("Veuillez suivre ce format: $templateFragment")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state = tooltipState2
|
||||||
|
) {
|
||||||
MyTextEditField(
|
MyTextEditField(
|
||||||
value = line,
|
value = displayedLine,
|
||||||
customFontSize = 13.sp,
|
customFontSize = 13.sp,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
customPadding = 8.dp,
|
customPadding = 8.dp,
|
||||||
customBrush = SolidColor(Color.White),
|
customBrush = SolidColor(Color.White),
|
||||||
isEditable = isEditable,
|
isEditable = isEditable,
|
||||||
isAddable = canAdd,
|
isAddable = canAdd,
|
||||||
|
isWarn = !isLyricsValid,
|
||||||
|
funTransform = { input -> transformLyricsInput(input, templateFragment) },
|
||||||
onValueChng = { newValue ->
|
onValueChng = { newValue ->
|
||||||
lyricsLines[index] = newValue
|
val trimmedValue = newValue.trimStart()
|
||||||
|
val dataToSave = if (trimmedValue.endsWith(" ")) trimmedValue else "$trimmedValue "
|
||||||
|
lyricsLines[index] = trimmedValue
|
||||||
|
val updatedLyricsMap = editState.lyricsByStanza.toMutableMap()
|
||||||
|
editedLyricsMap[index + 1] = dataToSave
|
||||||
|
|
||||||
|
val updatedState = editState.copy(lyricsByStanza = updatedLyricsMap)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isEditable || canAdd) {
|
if (isEditable || canAdd) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -443,7 +486,7 @@ fun TUODetailDialog(
|
||||||
}) {
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (index == 0) Icons.Default.Add else Icons.Default.Clear,
|
imageVector = if (index == 0) Icons.Default.Add else Icons.Default.Clear,
|
||||||
tint = if (index == 0) Color.Green else Color.Red,
|
tint = if (index == 0) MaterialTheme.colorScheme.secondary.copy(alpha = 1.5f) else MaterialTheme.colorScheme.tertiary.copy(alpha = 1.5f),
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -467,12 +510,17 @@ fun TUODetailDialog(
|
||||||
if (isEditable || canAdd) {
|
if (isEditable || canAdd) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
val finalLyricsMap = editState.lyricsByStanza.toMutableMap()
|
||||||
|
finalLyricsMap.putAll(editedLyricsMap)
|
||||||
|
if (!finalLyricsMap.containsKey(currentStanza)) {
|
||||||
|
finalLyricsMap[currentStanza] = lyricsLines.getOrNull(0)?.trim() ?: ""
|
||||||
|
}
|
||||||
val state = TUOEditState(
|
val state = TUOEditState(
|
||||||
tuoIndex = globalIndex,
|
tuoIndex = globalIndex,
|
||||||
notesByVoice = notes.toMap(),
|
notesByVoice = notes.toMap(),
|
||||||
originalNotes = originalNotes.toMap(),
|
originalNotes = originalNotes.toMap(),
|
||||||
originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
|
originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
|
||||||
lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")),
|
lyricsByStanza = finalLyricsMap,
|
||||||
templateFragment = templateFragment,
|
templateFragment = templateFragment,
|
||||||
marker = if(marker.isEmpty()) "_" else marker,
|
marker = if(marker.isEmpty()) "_" else marker,
|
||||||
originalSep = initialSep,
|
originalSep = initialSep,
|
||||||
|
|
@ -485,7 +533,7 @@ fun TUODetailDialog(
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Build,
|
Icons.Default.Build,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = Color.Green
|
tint = MaterialTheme.colorScheme.secondary.copy(alpha = 1.5f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -494,6 +542,102 @@ fun TUODetailDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun unpackLyrics(lyrics: String): String {
|
||||||
|
val comments = Solfa.REGEX_COMMENT.findAll(lyrics)
|
||||||
|
val commentsIterator = comments.iterator()
|
||||||
|
val loadedLyrics = lyrics
|
||||||
|
.replace(Solfa.REGEX_LYRICS_REPETITION) { matchResult ->
|
||||||
|
val repeating = matchResult.destructured.match.groupValues[1]
|
||||||
|
"_".repeat(repeating.toString().toInt())
|
||||||
|
}
|
||||||
|
.replace(Regex("(?<![\\?:,\\.; ])_"), "-_")
|
||||||
|
.replace(Regex("_-(?=_)"), "_")
|
||||||
|
.replace(" -_", "_")
|
||||||
|
.replace("--_", "-_")
|
||||||
|
.replace(Regex("_$"), "")
|
||||||
|
|
||||||
|
val lyricsFinal = Solfa.REGEX_COMMENT.replace(loadedLyrics) { matchResult ->
|
||||||
|
commentsIterator.next().value
|
||||||
|
}
|
||||||
|
return lyricsFinal
|
||||||
|
}
|
||||||
|
private fun smartYLyrics(lyrics: String): String {
|
||||||
|
val comments = Solfa.REGEX_COMMENT.findAll(lyrics)
|
||||||
|
val commentsIterator = comments.iterator()
|
||||||
|
val loadedLyrics = lyrics
|
||||||
|
.replace(Solfa.REGEX_VOWELS_STAGE1, "$0_")
|
||||||
|
.replace(Solfa.REGEX_VOWELS_STAGE2, "$1_")
|
||||||
|
.replace(Solfa.REGEX_VOWELS_STAGE3, "$1_")
|
||||||
|
.replace(" ", " _")
|
||||||
|
.replace("_\\ _", " ")
|
||||||
|
.replace("_\\", "")
|
||||||
|
.replace("_0", "")
|
||||||
|
.replace(Solfa.REGEX_MALAGASY_MN, "$1$2_$3")
|
||||||
|
.replace(Solfa.REGEX_MALAGASY_MN_STAGE2, "$1-_")
|
||||||
|
.replace("_n'", "n'_")
|
||||||
|
val lyricsFinal = Solfa.REGEX_COMMENT.replace(loadedLyrics) { matchResult ->
|
||||||
|
commentsIterator.next().value
|
||||||
|
}
|
||||||
|
return unpackLyrics(lyricsFinal)
|
||||||
|
}
|
||||||
|
public fun transformLyricsInput(input: String, template: String): String {
|
||||||
|
val templateCount = template.count { it.isLetter() && it.lowercaseChar() != 'z' }
|
||||||
|
|
||||||
|
val processed = smartYLyrics(input)
|
||||||
|
val currentSyllables = processed.split("_").filter { it.isNotEmpty() }
|
||||||
|
val currentCount = currentSyllables.size
|
||||||
|
|
||||||
|
// println("\n\n\tDEBUG [Start] Input: '$input' | TemplateCount: $templateCount | Found: $currentCount ${currentSyllables.joinToString("|")}")
|
||||||
|
// println("DEBUG [Segments]: $currentSyllables")
|
||||||
|
|
||||||
|
val rawResult = if (currentCount <= templateCount) {
|
||||||
|
// println("DEBUG [Status]: Pas de fusion nécessaire.")
|
||||||
|
input
|
||||||
|
} else {
|
||||||
|
val parts = currentSyllables.toMutableList()
|
||||||
|
val excess = currentCount - templateCount
|
||||||
|
// println("DEBUG [Fusion]: Besoin de fusionner $excess fois.")
|
||||||
|
|
||||||
|
for (i in 0 until excess) {
|
||||||
|
val idx = parts.size - 2/* - i*/
|
||||||
|
if (idx >= 0) {
|
||||||
|
// println("DEBUG [Loop $i]: Fusion de l'index $idx avec ${idx + 1}")
|
||||||
|
// println("DEBUG [Avant]: '${parts[idx]}' et '${parts[idx + 1]}'")
|
||||||
|
val part1 = parts[idx].trimEnd()
|
||||||
|
val part2 = parts[idx + 1].trimStart()
|
||||||
|
val cleanPart1 = part1.replace("-", "")
|
||||||
|
val lastChar = if (cleanPart1.isNotEmpty()) cleanPart1.last() else ' '
|
||||||
|
|
||||||
|
val endsWithVowel = "aeiouyòàéìỳAEIOUY".contains(lastChar)
|
||||||
|
val isVowelConsonantPair = endsWithVowel && part2.length == 1 && !"aeiouyòàéìỳAEIOUY".contains(part2.first())
|
||||||
|
|
||||||
|
if (part2.isNotEmpty()) {
|
||||||
|
if (isVowelConsonantPair) {
|
||||||
|
parts[idx] = part1 + part2
|
||||||
|
} else {
|
||||||
|
parts[idx] = part1 + "\\ " + part2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parts[idx] = part1 + "\\"
|
||||||
|
}
|
||||||
|
parts.removeAt(idx + 1)
|
||||||
|
}
|
||||||
|
// println("DEBUG [Après]: Résultat index $idx -> '${parts}'")
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = parts.joinToString("")
|
||||||
|
// println("DEBUG [Final]: '$result'")
|
||||||
|
result
|
||||||
|
}
|
||||||
|
val REGEX_MALAGASY_MN_OUTPUT = Regex("-([mn])")
|
||||||
|
val correctedResult = rawResult.replace(REGEX_MALAGASY_MN_OUTPUT, "$1-")
|
||||||
|
|
||||||
|
if (correctedResult != rawResult) {
|
||||||
|
// println("DEBUG [Correction MN]: '$rawResult' -> '$correctedResult'")
|
||||||
|
}
|
||||||
|
return correctedResult
|
||||||
|
}
|
||||||
private fun transformMusicalInput(input: String): String {
|
private fun transformMusicalInput(input: String): String {
|
||||||
return input.lowercase()
|
return input.lowercase()
|
||||||
.replace(";", "• ,")
|
.replace(";", "• ,")
|
||||||
|
|
@ -585,7 +729,7 @@ fun MyTextEditField(
|
||||||
.background(Color.Black.copy(alpha = 0.3f), RoundedCornerShape(4.dp))
|
.background(Color.Black.copy(alpha = 0.3f), RoundedCornerShape(4.dp))
|
||||||
.border(
|
.border(
|
||||||
if (isWarn!!) 1.dp else 0.dp,
|
if (isWarn!!) 1.dp else 0.dp,
|
||||||
if (isWarn!!) Color(0xFFF59E0B) else Color.Transparent,
|
if (isWarn!!) MaterialTheme.colorScheme.tertiary.copy(alpha = 1.5f) else Color.Transparent,
|
||||||
shape = RoundedCornerShape(4.dp)
|
shape = RoundedCornerShape(4.dp)
|
||||||
)
|
)
|
||||||
.padding(customPadding),
|
.padding(customPadding),
|
||||||
|
|
|
||||||
|
|
@ -268,11 +268,11 @@ fun TimeUnitComposable(
|
||||||
parentFocusRequester: FocusRequester? = null,
|
parentFocusRequester: FocusRequester? = null,
|
||||||
transpositionInterval: Int
|
transpositionInterval: Int
|
||||||
) {
|
) {
|
||||||
val col = if (tuo.getNum() % 2 == 0) Color(0xff, 0xfa, 0xf7) else Color(0xfb, 0xf3, 0xff)
|
val col = if (tuo.getNum() % 2 == 0) MaterialTheme.colorScheme.tertiary.copy(alpha = 0.05f) else MaterialTheme.colorScheme.error.copy(alpha = 0.05f)
|
||||||
val currentDensity = LocalDensity.current
|
val currentDensity = LocalDensity.current
|
||||||
|
|
||||||
val animatedColor by animateColorAsState(
|
val animatedColor by animateColorAsState(
|
||||||
targetValue = if (gridActive) Color.Cyan.copy(alpha = 0.5f) else col,
|
targetValue = if (gridActive) MaterialTheme.colorScheme.secondary.copy(alpha = 1f) else col,
|
||||||
animationSpec = tween(durationMillis = 100) // Très court pour rester réactif
|
animationSpec = tween(durationMillis = 100) // Très court pour rester réactif
|
||||||
)
|
)
|
||||||
val focusRequesters = remember { List(4) { FocusRequester() } }
|
val focusRequesters = remember { List(4) { FocusRequester() } }
|
||||||
|
|
@ -449,7 +449,7 @@ fun TimeUnitComposable(
|
||||||
fontSize = 10.sp,
|
fontSize = 10.sp,
|
||||||
baselineShift = BaselineShift.Superscript,
|
baselineShift = BaselineShift.Superscript,
|
||||||
//fontStyle = FontStyle.Italic,
|
//fontStyle = FontStyle.Italic,
|
||||||
color = FEUFAROO_KEY_CHANGE_COLOR
|
color = MaterialTheme.colorScheme.primary
|
||||||
)) {
|
)) {
|
||||||
append(text+" ")
|
append(text+" ")
|
||||||
}
|
}
|
||||||
|
|
@ -459,6 +459,7 @@ fun TimeUnitComposable(
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
text = annotatedText,
|
text = annotatedText,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
onTextLayout = { result ->
|
onTextLayout = { result ->
|
||||||
textLayoutResult = result
|
textLayoutResult = result
|
||||||
}
|
}
|
||||||
|
|
@ -768,8 +769,8 @@ fun LazyVerticalGridTUO(
|
||||||
fontStyle = fontStyle,
|
fontStyle = fontStyle,
|
||||||
fontWeight = fontWeight,
|
fontWeight = fontWeight,
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
color = Color.Black,
|
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||||
fontSize = 17.sp
|
fontSize = MaterialTheme.typography.titleMedium.fontSize
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -792,11 +793,11 @@ fun LazyVerticalGridTUO(
|
||||||
.width(gridWidthDp / gridColumnCount)
|
.width(gridWidthDp / gridColumnCount)
|
||||||
.border(
|
.border(
|
||||||
width = if (isSelectedByKeyboard && editMode) 2.dp else 0.dp,
|
width = if (isSelectedByKeyboard && editMode) 2.dp else 0.dp,
|
||||||
color = if (isSelectedByKeyboard && editMode) Color(0xFF9C27B0) else Color.Transparent,
|
color = if (isSelectedByKeyboard && editMode) MaterialTheme.colorScheme.error else Color.Transparent,
|
||||||
shape = RoundedCornerShape(4.dp)
|
shape = RoundedCornerShape(4.dp)
|
||||||
)
|
)
|
||||||
.background(
|
.background(
|
||||||
if (isHovered) Color.Black.copy(alpha = 0.05f) else Color.Transparent,
|
if (isHovered) MaterialTheme.colorScheme.onSecondary.copy(alpha = 0.05f) else Color.Transparent,
|
||||||
RoundedCornerShape(4.dp)
|
RoundedCornerShape(4.dp)
|
||||||
)
|
)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
|
|
@ -994,18 +995,18 @@ fun LazyVerticalGridTUO(
|
||||||
text = dynamicSpaceSyl,
|
text = dynamicSpaceSyl,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(end = 4.dp)/*.border(1.dp, Color.Yellow, RectangleShape)*/
|
.padding(end = 4.dp)
|
||||||
.wrapContentSize(unbounded = true, align = alignmentText),
|
.wrapContentSize(unbounded = true, align = alignmentText),
|
||||||
softWrap = false,
|
softWrap = false,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Visible,
|
overflow = TextOverflow.Visible,
|
||||||
style = TextStyle(
|
style = TextStyle(
|
||||||
color = when {
|
color = when {
|
||||||
(isTooLong) -> Color(0XFF121212)
|
(isTooLong) -> MaterialTheme.colorScheme.onBackground
|
||||||
(index == 0) -> Color.Black
|
(index == 0) -> MaterialTheme.colorScheme.onBackground
|
||||||
else -> Color(0XFF3B3A39)
|
else -> MaterialTheme.colorScheme.onBackground
|
||||||
},
|
},
|
||||||
fontSize = 16.sp,
|
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||||
),
|
),
|
||||||
onTextLayout = { txtLayoutRes ->
|
onTextLayout = { txtLayoutRes ->
|
||||||
textWidth = with(density) { txtLayoutRes.size.width.toDp() }
|
textWidth = with(density) { txtLayoutRes.size.width.toDp() }
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package mg.dot.feufaro.ui
|
||||||
|
|
||||||
import SharedScreenModel
|
import SharedScreenModel
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
|
@ -24,7 +26,10 @@ import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.input.pointer.PointerEventType
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.VisualTransformation
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
|
@ -169,6 +174,8 @@ fun MainScreenWithDrawer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isQrVisible = sharedScreenModel.isQRCodeVisible.value
|
||||||
|
|
||||||
ModalNavigationDrawer(drawerState = drawerState, drawerContent = {
|
ModalNavigationDrawer(drawerState = drawerState, drawerContent = {
|
||||||
SimpleDrawerContent(
|
SimpleDrawerContent(
|
||||||
items,
|
items,
|
||||||
|
|
@ -350,17 +357,20 @@ fun MainScreenWithDrawer(
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
softWrap = false,
|
softWrap = false,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
style = MaterialTheme.typography.headlineLarge
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, navigationIcon = {
|
}, navigationIcon = {
|
||||||
|
if(!isQrVisible) {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
scope.launch { drawerState.open() }
|
scope.launch { drawerState.open() }
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Filled.Menu, contentDescription = "Ouvrir Menu")
|
Icon(Icons.Filled.Menu, contentDescription = "Ouvrir Menu")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}, actions = {
|
}, actions = {
|
||||||
var tempInterval by remember(fileContent) { mutableStateOf(0) }
|
var tempInterval by remember(fileContent) { mutableStateOf(0) }
|
||||||
var isEyeVisible by remember { mutableStateOf(false) }
|
var isEyeVisible by remember { mutableStateOf(false) }
|
||||||
|
|
@ -382,7 +392,7 @@ fun MainScreenWithDrawer(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = tempUiKey,
|
text = tempUiKey,
|
||||||
fontSize = 25.sp,
|
style = MaterialTheme.typography.displaySmall,
|
||||||
fontWeight = FontWeight.Black,
|
fontWeight = FontWeight.Black,
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
color = if (isCurrentlyTransposed && !isPendingChange) Color(0xFFFFD700) else Color.White,
|
color = if (isCurrentlyTransposed && !isPendingChange) Color(0xFFFFD700) else Color.White,
|
||||||
|
|
@ -441,7 +451,7 @@ fun MainScreenWithDrawer(
|
||||||
}
|
}
|
||||||
|
|
||||||
val centralIcon = if (isPendingChange) Icons.Filled.Check else Icons.Filled.SwapHoriz
|
val centralIcon = if (isPendingChange) Icons.Filled.Check else Icons.Filled.SwapHoriz
|
||||||
val centralTint = if (isPendingChange) Color(0xFFFFD700) else Color.White
|
val centralTint = if (isPendingChange) MaterialTheme.colorScheme.tertiary else Color.White
|
||||||
val tooltipText = if (isPendingChange) "Transposer en $tempUiKey" else if (isCurrentlyTransposed) "Revenir en $songKey" else "Transposer"
|
val tooltipText = if (isPendingChange) "Transposer en $tempUiKey" else if (isCurrentlyTransposed) "Revenir en $songKey" else "Transposer"
|
||||||
|
|
||||||
TooltipBox(
|
TooltipBox(
|
||||||
|
|
@ -522,11 +532,9 @@ fun MainScreenWithDrawer(
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().height(36.dp),
|
modifier = Modifier.fillMaxWidth().height(36.dp),
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
border = BorderStroke(1.dp, Color(0xFFFFD700).copy(alpha = 0.5f)),
|
border = BorderStroke(1.dp, MaterialTheme.colorScheme.tertiary.copy(alpha = 0.5f)),
|
||||||
colors = ButtonDefaults.outlinedButtonColors(
|
colors = ButtonDefaults.outlinedButtonColors(
|
||||||
contentColor = Color(
|
contentColor = MaterialTheme.colorScheme.tertiary
|
||||||
0xFFFFD700
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
|
|
@ -571,7 +579,6 @@ fun MainScreenWithDrawer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
containerColor = Color(0x20000000),
|
|
||||||
icon = Icons.AutoMirrored.Default.Undo
|
icon = Icons.AutoMirrored.Default.Undo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -593,7 +600,6 @@ fun MainScreenWithDrawer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
containerColor = Color(0x20000000),
|
|
||||||
icon = Icons.Filled.SaveAs
|
icon = Icons.Filled.SaveAs
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -607,11 +613,10 @@ fun MainScreenWithDrawer(
|
||||||
onClick = {
|
onClick = {
|
||||||
sharedScreenModel.toggleEditorMode(false)
|
sharedScreenModel.toggleEditorMode(false)
|
||||||
},
|
},
|
||||||
containerColor = Color(0x20000000),
|
|
||||||
icon = Icons.Default.Close
|
icon = Icons.Default.Close
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else if(!isQrVisible) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = isExpanded and !showMidiCtrl,
|
visible = isExpanded and !showMidiCtrl,
|
||||||
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
|
enter = fadeIn() + scaleIn() + slideInVertically { it / 2 },
|
||||||
|
|
@ -623,7 +628,6 @@ fun MainScreenWithDrawer(
|
||||||
onClick = {
|
onClick = {
|
||||||
sharedScreenModel.descGridCount(1)
|
sharedScreenModel.descGridCount(1)
|
||||||
},
|
},
|
||||||
containerColor = Color(0x15000000),
|
|
||||||
size = 30.dp,
|
size = 30.dp,
|
||||||
icon = Icons.Default.Remove
|
icon = Icons.Default.Remove
|
||||||
)
|
)
|
||||||
|
|
@ -633,7 +637,6 @@ fun MainScreenWithDrawer(
|
||||||
onClick = {
|
onClick = {
|
||||||
sharedScreenModel.addGridCount(1)
|
sharedScreenModel.addGridCount(1)
|
||||||
},
|
},
|
||||||
containerColor = Color(0x15000000),
|
|
||||||
size = 30.dp,
|
size = 30.dp,
|
||||||
icon = Icons.Default.Add
|
icon = Icons.Default.Add
|
||||||
)
|
)
|
||||||
|
|
@ -650,7 +653,6 @@ fun MainScreenWithDrawer(
|
||||||
sharedScreenModel.toggleQRCodeVisibility()
|
sharedScreenModel.toggleQRCodeVisibility()
|
||||||
sharedScreenModel.setExpandedFAB(false)
|
sharedScreenModel.setExpandedFAB(false)
|
||||||
},
|
},
|
||||||
containerColor = Color(0x15000000),
|
|
||||||
icon = Icons.Filled.QrCode
|
icon = Icons.Filled.QrCode
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -665,7 +667,6 @@ fun MainScreenWithDrawer(
|
||||||
onClick = {
|
onClick = {
|
||||||
showPrintSettings = !showPrintSettings
|
showPrintSettings = !showPrintSettings
|
||||||
},
|
},
|
||||||
containerColor = Color(0x15000000),
|
|
||||||
icon = Icons.Filled.Print
|
icon = Icons.Filled.Print
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -679,7 +680,6 @@ fun MainScreenWithDrawer(
|
||||||
onClick = {
|
onClick = {
|
||||||
sharedScreenModel.setMidiCtrl(!showMidiCtrl)
|
sharedScreenModel.setMidiCtrl(!showMidiCtrl)
|
||||||
},
|
},
|
||||||
containerColor = Color(0x15000000),
|
|
||||||
icon = if (showMidiCtrl) Icons.Filled.StopCircle else Icons.Filled.PlayCircle
|
icon = if (showMidiCtrl) Icons.Filled.StopCircle else Icons.Filled.PlayCircle
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -690,7 +690,6 @@ fun MainScreenWithDrawer(
|
||||||
refreshTrigeer++
|
refreshTrigeer++
|
||||||
sharedScreenModel.loadNewSong("$midiFile")
|
sharedScreenModel.loadNewSong("$midiFile")
|
||||||
},
|
},
|
||||||
containerColor = if (isExpanded) Color(0x25000000) else Color.Transparent,
|
|
||||||
icon = if (isExpanded) Icons.Filled.Close else Icons.Filled.Menu
|
icon = if (isExpanded) Icons.Filled.Close else Icons.Filled.Menu
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -748,17 +747,42 @@ fun MainScreenWithDrawer(
|
||||||
/*.windowInsetsPadding(currentInsets.union(WindowInsets.ime))*/
|
/*.windowInsetsPadding(currentInsets.union(WindowInsets.ime))*/
|
||||||
) {
|
) {
|
||||||
content(PaddingValues(0.dp))
|
content(PaddingValues(0.dp))
|
||||||
if (sharedScreenModel.isQRCodeVisible.value) {
|
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = isQrVisible,
|
||||||
|
enter = slideInVertically(
|
||||||
|
initialOffsetY = { fullHeight -> fullHeight }
|
||||||
|
) + fadeIn(),
|
||||||
|
exit = slideOutVertically(
|
||||||
|
targetOffsetY = { fullHeight -> fullHeight }
|
||||||
|
) + fadeOut()
|
||||||
|
) {
|
||||||
QRDisplay(
|
QRDisplay(
|
||||||
sharedScreenModel = sharedScreenModel,
|
sharedScreenModel = sharedScreenModel,
|
||||||
fileRepository = solfaScreenModel.fileRepository
|
fileRepository = solfaScreenModel.fileRepository
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isHovered by remember { mutableStateOf(false) }
|
||||||
|
val offsetX by animateDpAsState(targetValue = if (isHovered || isSearchActive) 0.dp else 15.dp)
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = !isEditMode,
|
visible = !isEditMode && !isQrVisible,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopEnd)
|
.align(Alignment.TopEnd)
|
||||||
.padding(16.dp)
|
.padding(top = 15.dp)
|
||||||
|
.offset(x = offsetX)
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
awaitPointerEventScope {
|
||||||
|
while (true) {
|
||||||
|
val event = awaitPointerEvent()
|
||||||
|
when (event.type) {
|
||||||
|
PointerEventType.Enter -> isHovered = true
|
||||||
|
PointerEventType.Exit -> isHovered = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -778,14 +802,14 @@ fun MainScreenWithDrawer(
|
||||||
.size(55.dp)
|
.size(55.dp)
|
||||||
.alpha(0.6f)
|
.alpha(0.6f)
|
||||||
.background(
|
.background(
|
||||||
color = Color.Blue,
|
color = MaterialTheme.colorScheme.tertiary,
|
||||||
shape = CircleShape
|
shape = CircleShape
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (isSearchActive) Icons.Default.Close else Icons.Default.Search,
|
imageVector = if (isSearchActive) Icons.Default.Close else Icons.Default.Search,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = Color.White
|
tint = MaterialTheme.colorScheme.onTertiary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -884,20 +908,19 @@ fun MainScreenWithDrawer(
|
||||||
@Composable
|
@Composable
|
||||||
private fun MyFAB(
|
private fun MyFAB(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
containerColor: Color,
|
|
||||||
size: Dp ?= 55.dp,
|
size: Dp ?= 55.dp,
|
||||||
icon: ImageVector
|
icon: ImageVector
|
||||||
) {
|
) {
|
||||||
FloatingActionButton(
|
FloatingActionButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = Modifier.size(size!!),
|
modifier = Modifier.size(size!!),
|
||||||
containerColor = containerColor,
|
containerColor = MaterialTheme.colorScheme.onSecondary.copy(alpha = 0.1f),
|
||||||
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp)
|
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = Color.Blue.copy(alpha = 0.75f)
|
tint = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.paint
|
import androidx.compose.ui.draw.paint
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.graphics.TransformOrigin
|
import androidx.compose.ui.graphics.TransformOrigin
|
||||||
|
|
@ -121,8 +122,25 @@ fun MidiControlPanel(
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.padding(24.dp)
|
||||||
.fillMaxWidth(if (platform.startsWith("Android")) 1f else 0.6f)
|
.fillMaxWidth(if (platform.startsWith("Android")) 1f else 0.6f)
|
||||||
.background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)),
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.onSecondary.copy(alpha = 0.12f),
|
||||||
|
shape = RoundedCornerShape(24.dp)
|
||||||
|
)
|
||||||
|
.border(
|
||||||
|
width = 1.dp,
|
||||||
|
color = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.8f),
|
||||||
|
shape = RoundedCornerShape(24.dp)
|
||||||
|
)
|
||||||
|
.shadow(
|
||||||
|
elevation = 40.dp,
|
||||||
|
shape = RoundedCornerShape(24.dp),
|
||||||
|
clip = false,
|
||||||
|
ambientColor = Color.Black.copy(alpha = 0.1f),
|
||||||
|
spotColor = Color.Black.copy(alpha = 0.3f)
|
||||||
|
),
|
||||||
|
/*.background(color = Color.Gray.copy(alpha = 0.5f), shape = RoundedCornerShape(size = 5.dp)),*/
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
|
|
@ -130,40 +148,43 @@ fun MidiControlPanel(
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
) {
|
) {
|
||||||
Text("${currentPos.toInt() / 1000}s", color = Color.White)
|
Text("${currentPos.toInt() / 1000}s", color = MaterialTheme.colorScheme.onSecondary, fontSize = MaterialTheme.typography.titleSmall.fontSize)
|
||||||
Slider(
|
Slider(
|
||||||
value = currentPos,
|
value = currentPos,
|
||||||
onValueChange = onSeek,
|
onValueChange = onSeek,
|
||||||
valueRange = 0f..(if (duration > 0) duration else 1f),
|
valueRange = 0f..(if (duration > 0) duration else 1f),
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
colors = SliderDefaults.colors(
|
colors = SliderDefaults.colors(
|
||||||
thumbColor = Color.Red,
|
activeTrackColor = MaterialTheme.colorScheme.primary,
|
||||||
activeTrackColor = Color.Green,
|
inactiveTrackColor = Color.White.copy(alpha = 0.2f),
|
||||||
inactiveTrackColor = Color.Gray,
|
thumbColor = MaterialTheme.colorScheme.secondary,
|
||||||
inactiveTickColor = Color(0xffb0BEC5),
|
disabledThumbColor = Color.Gray,
|
||||||
disabledThumbColor = Color(0xff78909C),
|
disabledActiveTrackColor = Color.Gray.copy(alpha = 0.5f)
|
||||||
disabledActiveTickColor = Color(0xff757575),
|
|
||||||
disabledActiveTrackColor = Color(0xffBDBDBD),
|
|
||||||
disabledInactiveTickColor = Color(0xff616161),
|
|
||||||
disabledInactiveTrackColor = Color(0xffBCAAA4),
|
|
||||||
),
|
),
|
||||||
thumb = {
|
thumb = {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(15.dp)
|
.size(12.dp)
|
||||||
.background(Color.Gray, CircleShape)
|
.background(MaterialTheme.colorScheme.secondary, CircleShape)
|
||||||
|
.shadow(4.dp, CircleShape)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
track = { sliderState ->
|
track = { sliderState ->
|
||||||
SliderDefaults.Track(
|
SliderDefaults.Track(
|
||||||
sliderState = sliderState,
|
sliderState = sliderState,
|
||||||
modifier = Modifier.height(5.dp)
|
modifier = Modifier.height(6.dp).clip(CircleShape),
|
||||||
|
colors = SliderDefaults.colors(
|
||||||
|
activeTrackColor = MaterialTheme.colorScheme.primary,
|
||||||
|
inactiveTrackColor = MaterialTheme.colorScheme.onSecondary.copy(alpha = 0.6f)
|
||||||
|
),
|
||||||
|
drawStopIndicator = null,
|
||||||
|
thumbTrackGapSize = 2.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Text("${momo / 1000}s", color = Color.White)
|
Text("${momo / 1000}s", color = MaterialTheme.colorScheme.onSecondary, fontSize = MaterialTheme.typography.titleSmall.fontSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,11 +300,16 @@ fun MidiControlPanel(
|
||||||
modifier = Modifier.padding(horizontal = 2.dp)
|
modifier = Modifier.padding(horizontal = 2.dp)
|
||||||
) { displayedBpm ->
|
) { displayedBpm ->
|
||||||
val isCenter = displayedBpm == currentBpmInt
|
val isCenter = displayedBpm == currentBpmInt
|
||||||
|
// 1. On prépare le style de manière propre en dehors du Text
|
||||||
|
val bpmTextStyle = if (isCenter) {
|
||||||
|
MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Bold)
|
||||||
|
} else {
|
||||||
|
MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Normal)
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
text = "$displayedBpm",
|
text = displayedBpm.toString(),
|
||||||
color = if (isCenter) Color.White else Color.Gray.copy(alpha = 0.4f),
|
color = if (isCenter) Color.White else Color.Gray.copy(alpha = 0.4f),
|
||||||
fontSize = if (isCenter) 22.sp else 14.sp,
|
style = bpmTextStyle,
|
||||||
fontWeight = if (isCenter) FontWeight.Bold else FontWeight.Normal,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.clickable(enabled = !isCenter) { updateTempoToBpm(displayedBpm) }
|
.clickable(enabled = !isCenter) { updateTempoToBpm(displayedBpm) }
|
||||||
|
|
@ -308,7 +334,7 @@ fun MidiControlPanel(
|
||||||
Text(
|
Text(
|
||||||
text = "bpm",
|
text = "bpm",
|
||||||
color = Color.White.copy(0.6f),
|
color = Color.White.copy(0.6f),
|
||||||
fontSize = 12.sp,
|
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
|
||||||
modifier = Modifier.padding(end = 8.dp)
|
modifier = Modifier.padding(end = 8.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -340,8 +366,8 @@ fun MidiControlPanel(
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors = ButtonDefaults.buttonColors(
|
||||||
containerColor = when {
|
containerColor = when {
|
||||||
(isWaitingForB || isLooping) -> Color.Red
|
(isWaitingForB || isLooping) -> MaterialTheme.colorScheme.error
|
||||||
else -> Color.LightGray
|
else -> MaterialTheme.colorScheme.onError
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
contentPadding = PaddingValues(horizontal = 2.dp, vertical = 0.dp),
|
contentPadding = PaddingValues(horizontal = 2.dp, vertical = 0.dp),
|
||||||
|
|
@ -356,7 +382,7 @@ fun MidiControlPanel(
|
||||||
Text(
|
Text(
|
||||||
text = "A",
|
text = "A",
|
||||||
color = if (isWaitingForB || isLooping) Color.White else Color.Black,
|
color = if (isWaitingForB || isLooping) Color.White else Color.Black,
|
||||||
fontSize = 16.sp
|
fontSize = MaterialTheme.typography.titleLarge.fontSize
|
||||||
)
|
)
|
||||||
|
|
||||||
Icon(
|
Icon(
|
||||||
|
|
@ -369,7 +395,7 @@ fun MidiControlPanel(
|
||||||
Text(
|
Text(
|
||||||
text = "B",
|
text = "B",
|
||||||
color = if (isLooping) Color.White else Color.Black,
|
color = if (isLooping) Color.White else Color.Black,
|
||||||
fontSize = 16.sp
|
fontSize = MaterialTheme.typography.titleLarge.fontSize
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -388,106 +414,60 @@ fun MidiControlPanel(
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(Res.drawable.ic_mixer_satb),
|
painter = painterResource(Res.drawable.ic_mixer_satb),
|
||||||
contentDescription = "SATB",
|
contentDescription = "SATB",
|
||||||
tint = if (showSATBTools) Color.White else Color.Black,
|
tint = if (showSATBTools) MaterialTheme.colorScheme.background else MaterialTheme.colorScheme.onSecondary,
|
||||||
modifier = Modifier.size(35.dp)
|
modifier = Modifier.size(35.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
if(platform.startsWith("Android")) {
|
val playPauseButton = @Composable {
|
||||||
Column(
|
|
||||||
modifier = Modifier.wrapContentWidth(),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onPlayPauseClick,
|
onClick = onPlayPauseClick,
|
||||||
modifier = Modifier.size(48.dp).background(MaterialTheme.colorScheme.primary, CircleShape)
|
modifier = Modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.background(MaterialTheme.colorScheme.primary, CircleShape)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause,
|
imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause,
|
||||||
contentDescription = "Pla",
|
contentDescription = "Play/Pause",
|
||||||
tint = Color.White
|
tint = Color.White
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
val instrumentButton = @Composable {
|
||||||
|
|
||||||
Column {
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
isPianoSelected = !isPianoSelected
|
isPianoSelected = !isPianoSelected
|
||||||
if (isPianoSelected) {
|
mediaPlayer?.changeInstru(if (isPianoSelected) 1 else 20)
|
||||||
mediaPlayer?.changeInstru(1)
|
|
||||||
} else {
|
|
||||||
mediaPlayer?.changeInstru(20)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (isPianoSelected) {
|
if (isPianoSelected) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Default.Piano,
|
imageVector = Icons.Default.Piano,
|
||||||
contentDescription = "Piano",
|
contentDescription = "Piano",
|
||||||
tint = Color.Black
|
tint = MaterialTheme.colorScheme.onSecondary
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(Res.drawable.ic_organ),
|
painter = painterResource(Res.drawable.ic_organ),
|
||||||
contentDescription = "Orgue",
|
contentDescription = "Orgue",
|
||||||
tint = Color.Black,
|
tint = MaterialTheme.colorScheme.onSecondary,
|
||||||
modifier = Modifier.size(25.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Column {
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
isPianoSelected = !isPianoSelected
|
|
||||||
if (isPianoSelected) {
|
|
||||||
mediaPlayer?.changeInstru(1)
|
|
||||||
} else {
|
|
||||||
mediaPlayer?.changeInstru(20)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
if (isPianoSelected) {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Piano,
|
|
||||||
contentDescription = "Piano",
|
|
||||||
tint = Color.Black
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Icon(
|
|
||||||
painter = painterResource(Res.drawable.ic_organ),
|
|
||||||
contentDescription = "Orgue",
|
|
||||||
tint = Color.Black,
|
|
||||||
modifier = Modifier.size(25.dp)
|
modifier = Modifier.size(25.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (platform.startsWith("Android")) {
|
||||||
|
playPauseButton()
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
Column(
|
instrumentButton()
|
||||||
modifier = Modifier.wrapContentWidth(),
|
} else {
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
instrumentButton()
|
||||||
) {
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
IconButton(
|
playPauseButton()
|
||||||
onClick = onPlayPauseClick,
|
|
||||||
modifier = Modifier.size(48.dp).background(MaterialTheme.colorScheme.primary, CircleShape)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = if (isPause) Icons.Filled.PlayArrow else Icons.Filled.Pause,
|
|
||||||
contentDescription = "Pla",
|
|
||||||
tint = Color.White
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -512,7 +492,7 @@ fun MidiControlPanel(
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (expandedCtl) Icons.Default.MoreHoriz else Icons.Default.MoreVert,
|
imageVector = if (expandedCtl) Icons.Default.MoreHoriz else Icons.Default.MoreVert,
|
||||||
contentDescription = "More",
|
contentDescription = "More",
|
||||||
tint = Color.White
|
tint = MaterialTheme.colorScheme.onSecondary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.compose.material.icons.automirrored.filled.VolumeOff
|
||||||
import androidx.compose.material.icons.automirrored.filled.VolumeUp
|
import androidx.compose.material.icons.automirrored.filled.VolumeUp
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.SliderDefaults
|
import androidx.compose.material3.SliderDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -34,7 +35,7 @@ fun ModernVolumeSlider(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(42.dp)
|
.height(42.dp)
|
||||||
.background(Color.White.copy(alpha = 0.1f), CircleShape)
|
.background(MaterialTheme.colorScheme.onSecondary.copy(alpha = 0.1f), CircleShape)
|
||||||
.padding(horizontal = 12.dp, vertical = 4.dp)
|
.padding(horizontal = 12.dp, vertical = 4.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|
@ -44,7 +45,7 @@ fun ModernVolumeSlider(
|
||||||
else -> Icons.AutoMirrored.Filled.VolumeUp
|
else -> Icons.AutoMirrored.Filled.VolumeUp
|
||||||
},
|
},
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = if (volume > 0) Color.Black else Color.Gray,
|
tint = if (volume > 0) MaterialTheme.colorScheme.onSecondary else Color.Gray,
|
||||||
modifier = Modifier.size(20.dp)
|
modifier = Modifier.size(20.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -55,8 +56,8 @@ fun ModernVolumeSlider(
|
||||||
onValueChange = onVolumeChange,
|
onValueChange = onVolumeChange,
|
||||||
modifier = Modifier.fillMaxWidth(0.30f),
|
modifier = Modifier.fillMaxWidth(0.30f),
|
||||||
colors = SliderDefaults.colors(
|
colors = SliderDefaults.colors(
|
||||||
activeTrackColor = Color(0xFFF59E0B),
|
activeTrackColor = MaterialTheme.colorScheme.primary,
|
||||||
inactiveTrackColor = Color.White.copy(alpha = 0.2f),
|
inactiveTrackColor = MaterialTheme.colorScheme.onSecondary.copy(alpha = 0.6f),
|
||||||
thumbColor = Color.Transparent
|
thumbColor = Color.Transparent
|
||||||
),
|
),
|
||||||
track = { sliderState ->
|
track = { sliderState ->
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,16 @@ import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
|
|
@ -26,6 +30,7 @@ import androidx.compose.ui.unit.sp
|
||||||
import feufaro.composeapp.generated.resources.Res
|
import feufaro.composeapp.generated.resources.Res
|
||||||
import feufaro.composeapp.generated.resources.compose_multiplatform
|
import feufaro.composeapp.generated.resources.compose_multiplatform
|
||||||
import mg.dot.feufaro.FileRepository
|
import mg.dot.feufaro.FileRepository
|
||||||
|
import mg.dot.feufaro.MGButton
|
||||||
import mg.dot.feufaro.solfa.ColorPrefixA
|
import mg.dot.feufaro.solfa.ColorPrefixA
|
||||||
import mg.dot.feufaro.solfa.ColorPrefixB
|
import mg.dot.feufaro.solfa.ColorPrefixB
|
||||||
import mg.dot.feufaro.solfa.ColorValue
|
import mg.dot.feufaro.solfa.ColorValue
|
||||||
|
|
@ -62,48 +67,59 @@ fun QRDisplay(sharedScreenModel: SharedScreenModel, fileRepository: FileReposito
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(Color.Black.copy(alpha = 0.6f)) // Assombrissement moderne
|
.background(Color.Black.copy(alpha = 0.4f))
|
||||||
.clickable { sharedScreenModel.toggleQRCodeVisibility() } // Fermer au clic
|
.clickable { sharedScreenModel.toggleQRCodeVisibility() },
|
||||||
.imePadding()
|
contentAlignment = Alignment.BottomCenter
|
||||||
.systemBarsPadding(),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
) {
|
||||||
|
BoxWithConstraints(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.BottomCenter
|
||||||
|
) {
|
||||||
|
val screenHeight = maxHeight
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(400.dp)
|
.fillMaxWidth()
|
||||||
.wrapContentHeight()
|
.widthIn(max = 450.dp)
|
||||||
.shadow(24.dp, shape = RoundedCornerShape(28.dp)),
|
.height(screenHeight * 0.85f)
|
||||||
shape = RoundedCornerShape(28.dp),
|
.clickable(enabled = false) {}
|
||||||
|
.shadow(16.dp, shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp)),
|
||||||
|
shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = Modifier.padding(24.dp)
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.padding(top = 12.dp, bottom = 32.dp)
|
||||||
) {
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(width = 40.dp, height = 4.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f))
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Partager la partition",
|
text = "Scanner ce QR Code",
|
||||||
style = MaterialTheme.typography.titleLarge.copy(
|
style = MaterialTheme.typography.titleLarge.copy(
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
letterSpacing = 0.5.sp
|
fontSize = 22.sp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Spacer(modifier = Modifier.height(5.dp))
|
||||||
text = "Scannez pour ouvrir instantanément",
|
|
||||||
style = MaterialTheme.typography.bodyMedium.copy(
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
),
|
|
||||||
modifier = Modifier.padding(top = 4.dp, bottom = 24.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (qrCodeImage != null) {
|
if (qrCodeImage != null) {
|
||||||
Box(
|
Box(
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(350.dp)
|
.weight(10f)
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.fillMaxHeight(1f)
|
||||||
.background(Color.White)
|
.aspectRatio(1f)
|
||||||
.padding(5.dp)
|
.clip(RoundedCornerShape(24.dp))
|
||||||
|
.padding(12.dp)
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
bitmap = qrCodeImage!!,
|
bitmap = qrCodeImage!!,
|
||||||
|
|
@ -113,7 +129,8 @@ fun QRDisplay(sharedScreenModel: SharedScreenModel, fileRepository: FileReposito
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(54.dp)
|
.fillMaxHeight(0.18f)
|
||||||
|
.aspectRatio(1f)
|
||||||
.background(Color.White, shape = RoundedCornerShape(12.dp))
|
.background(Color.White, shape = RoundedCornerShape(12.dp))
|
||||||
.padding(4.dp),
|
.padding(4.dp),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
|
|
@ -143,15 +160,36 @@ fun QRDisplay(sharedScreenModel: SharedScreenModel, fileRepository: FileReposito
|
||||||
Text(
|
Text(
|
||||||
text = "Génération du QR Code...",
|
text = "Génération du QR Code...",
|
||||||
style = MaterialTheme.typography.bodyMedium.copy(
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
color = ColorValue,
|
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||||
fontWeight = FontWeight.Medium
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Spacer(modifier = Modifier.height(2.dp))
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Text(
|
||||||
|
text = "Partager et charger rapidement cette partition",
|
||||||
|
style = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f),
|
||||||
|
letterSpacing = 0.2.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(2.dp))
|
||||||
|
Button(
|
||||||
|
onClick = { sharedScreenModel.toggleQRCodeVisibility() },
|
||||||
|
modifier = Modifier
|
||||||
|
.width(450.dp),
|
||||||
|
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Fermer",
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
package mg.dot.feufaro.ui
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import SharedScreenModel
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||||
|
import androidx.compose.material.icons.filled.DarkMode
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
|
import androidx.compose.material.icons.filled.LightMode
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
|
@ -20,10 +25,12 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import SharedScreenModel
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import mg.dot.feufaro.DisplayConfigManager
|
||||||
import mg.dot.feufaro.getPlatform
|
import mg.dot.feufaro.getPlatform
|
||||||
import mg.dot.feufaro.midi.Dynamic
|
import mg.dot.feufaro.midi.Dynamic
|
||||||
|
import org.koin.compose.koinInject
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -39,6 +46,22 @@ fun Settings(
|
||||||
val isFullScreenEnabled by sharedScreenModel.isFullScreen.collectAsState()
|
val isFullScreenEnabled by sharedScreenModel.isFullScreen.collectAsState()
|
||||||
val platform = getPlatform()
|
val platform = getPlatform()
|
||||||
val isAndroid = platform.name.startsWith("Android")
|
val isAndroid = platform.name.startsWith("Android")
|
||||||
|
|
||||||
|
// Load Configurations
|
||||||
|
val displayConfigManager = koinInject<DisplayConfigManager>()
|
||||||
|
val currentDisplayConfig by displayConfigManager.displayConfig.collectAsState()
|
||||||
|
|
||||||
|
val currentTheme = currentDisplayConfig.themeMode
|
||||||
|
val isDarkModeActive = currentTheme == "DARK"
|
||||||
|
val configScope = CoroutineScope(Dispatchers.Default)
|
||||||
|
|
||||||
|
var tempDarkMode by remember(currentDisplayConfig.themeMode) {
|
||||||
|
mutableStateOf(currentDisplayConfig.themeMode == "DARK")
|
||||||
|
}
|
||||||
|
var tempFontSize by remember(currentDisplayConfig.fontSize) {
|
||||||
|
mutableStateOf(currentDisplayConfig.fontSize)
|
||||||
|
}
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
title = {
|
title = {
|
||||||
|
|
@ -63,8 +86,8 @@ fun Settings(
|
||||||
onToggle = { expandedGeneral = !expandedGeneral }
|
onToggle = { expandedGeneral = !expandedGeneral }
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(8.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
if(isAndroid) {
|
if(isAndroid) {
|
||||||
Row(
|
Row(
|
||||||
|
|
@ -81,6 +104,109 @@ fun Settings(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 4.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Thème Sombre",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
|
||||||
|
Switch(
|
||||||
|
checked = tempDarkMode,
|
||||||
|
onCheckedChange = { checked ->
|
||||||
|
val newMode = if (checked) "DARK" else "LIGHT"
|
||||||
|
displayConfigManager.updateThemeMode(newMode)
|
||||||
|
},
|
||||||
|
thumbContent = {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (tempDarkMode) Icons.Default.DarkMode else Icons.Default.LightMode,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onSecondary
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = SwitchDefaults.colors(
|
||||||
|
checkedThumbColor = MaterialTheme.colorScheme.primary,
|
||||||
|
checkedTrackColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.4f),
|
||||||
|
uncheckedThumbColor = MaterialTheme.colorScheme.error,
|
||||||
|
uncheckedTrackColor = MaterialTheme.colorScheme.error.copy(alpha = 0.2f),
|
||||||
|
checkedBorderColor = Color.Transparent,
|
||||||
|
uncheckedBorderColor = Color.Transparent,
|
||||||
|
checkedIconColor = Color.White,
|
||||||
|
uncheckedIconColor = MaterialTheme.colorScheme.onTertiary,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider(color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 4.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Taille de la police",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(15.dp)
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
if (currentDisplayConfig.fontSize > 8f) {
|
||||||
|
displayConfigManager.updateFontSize(currentDisplayConfig.fontSize - 0.5f)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.size(25.dp).background(MaterialTheme.colorScheme.primary, CircleShape)
|
||||||
|
) {
|
||||||
|
Text("-", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.titleLarge, color = Color.White)
|
||||||
|
}
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = tempFontSize.toString(),
|
||||||
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
|
textStyle = MaterialTheme.typography.titleMedium.copy(textAlign = TextAlign.Center),
|
||||||
|
modifier = Modifier.width(80.dp),
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
if (currentDisplayConfig.fontSize < 30f) {
|
||||||
|
displayConfigManager.updateFontSize(currentDisplayConfig.fontSize + 0.5f)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.size(25.dp).background(MaterialTheme.colorScheme.primary, CircleShape)
|
||||||
|
) {
|
||||||
|
Text("+", fontWeight = FontWeight.Bold, style = MaterialTheme.typography.titleLarge, color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = { displayConfigManager.resetToDefault() },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
colors = ButtonDefaults.outlinedButtonColors(
|
||||||
|
contentColor = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text("Réinitialiser", style = MaterialTheme.typography.labelLarge)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,7 +271,7 @@ fun Settings(
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.AutoMirrored.Default.Undo,
|
imageVector = Icons.AutoMirrored.Default.Undo,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = Color.Red
|
tint = MaterialTheme.colorScheme.error
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package mg.dot.feufaro.ui
|
||||||
|
|
||||||
import SharedScreenModel
|
import SharedScreenModel
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
|
@ -13,9 +12,8 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.EventNote
|
import androidx.compose.material.icons.automirrored.filled.ListAlt
|
||||||
import androidx.compose.material.icons.automirrored.filled.Note
|
import androidx.compose.material.icons.automirrored.filled.PlaylistPlay
|
||||||
import androidx.compose.material.icons.automirrored.filled.StarHalf
|
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
|
@ -25,7 +23,6 @@ import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.draw.scale
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.text.TextStyle
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
@ -87,23 +84,28 @@ fun SimpleDrawerContent(
|
||||||
onCheckedChange = { newState ->
|
onCheckedChange = { newState ->
|
||||||
sharedScreenModel.toggleEditorMode(newState)
|
sharedScreenModel.toggleEditorMode(newState)
|
||||||
},
|
},
|
||||||
label = "Mode Edit",
|
thumbIcon = Icons.Default.Edit,
|
||||||
color = MaterialTheme.colorScheme.primary
|
label = "Mode Edit"
|
||||||
)
|
)
|
||||||
|
|
||||||
CustomSwitchItem(
|
CustomSwitchItem(
|
||||||
checked = state1,
|
checked = state1,
|
||||||
onCheckedChange = { state1 = it },
|
onCheckedChange = { state1 = it },
|
||||||
|
thumbIcon = Icons.Default.MusicNote,
|
||||||
label = "Analyse chords",
|
label = "Analyse chords",
|
||||||
color = Color(0xFFE57373)
|
|
||||||
)
|
|
||||||
|
|
||||||
CustomSwitchItem(
|
|
||||||
checked = state2,
|
|
||||||
onCheckedChange = { state2 = it },
|
|
||||||
label = "Titre 3",
|
|
||||||
color = Color(0xFFBFBF11)
|
|
||||||
)
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(5.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = { },
|
||||||
|
modifier = Modifier.background(Color(0xFF81C784), shape = CircleShape).size(40.dp)
|
||||||
|
) {
|
||||||
|
Icon(Icons.Default.Add, contentDescription = null, tint = Color.White)
|
||||||
|
}
|
||||||
|
Text("Ajouter", style = MaterialTheme.typography.labelSmall)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,8 +125,7 @@ fun SimpleDrawerContent(
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
DrawerHeaderSticky(
|
DrawerHeaderSticky(
|
||||||
title = "Solfa disponibles",
|
title = "Solfa disponibles",
|
||||||
icon = Icons.AutoMirrored.Filled.Note,
|
icon = Icons.AutoMirrored.Filled.ListAlt,
|
||||||
color = MaterialTheme.colorScheme.primary,
|
|
||||||
isExpanded = internalExpanded,
|
isExpanded = internalExpanded,
|
||||||
onToggle = { internalExpanded = !internalExpanded },
|
onToggle = { internalExpanded = !internalExpanded },
|
||||||
count = internalList.size
|
count = internalList.size
|
||||||
|
|
@ -134,11 +135,29 @@ fun SimpleDrawerContent(
|
||||||
if (internalExpanded) {
|
if (internalExpanded) {
|
||||||
items(internalList) { item ->
|
items(internalList) { item ->
|
||||||
val isSelected = item.path == activePath
|
val isSelected = item.path == activePath
|
||||||
|
val favoriteLists by sharedScreenModel.playlistItems.collectAsState()
|
||||||
|
val isFavorite = favoriteLists.contains(item)
|
||||||
|
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
icon = {
|
icon = {
|
||||||
Text("\uD834\uDD1E", fontSize = 18.sp)
|
Text("\uD834\uDD1E", fontSize = 22.sp)
|
||||||
|
},
|
||||||
|
label = { DrawerItemLabel(item) },
|
||||||
|
badge = {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
sharedScreenModel.toggleFavorite(item.path)
|
||||||
|
},
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.PlaylistAddCircle,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = if (!isFavorite) Color.LightGray else Color(0xFFFFD700),
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
label = { DrawerItemLabel(item, sharedScreenModel) },
|
|
||||||
selected = isSelected,
|
selected = isSelected,
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
|
|
@ -149,8 +168,8 @@ fun SimpleDrawerContent(
|
||||||
shape = RoundedCornerShape(5.dp),
|
shape = RoundedCornerShape(5.dp),
|
||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp),
|
modifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp),
|
||||||
colors = NavigationDrawerItemDefaults.colors(
|
colors = NavigationDrawerItemDefaults.colors(
|
||||||
selectedContainerColor = Color(0, 157, 255).copy(alpha = 0.1f),
|
selectedContainerColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.12f),
|
||||||
selectedTextColor = Color(0, 157, 255)
|
selectedTextColor = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -159,8 +178,7 @@ fun SimpleDrawerContent(
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
DrawerHeaderSticky(
|
DrawerHeaderSticky(
|
||||||
title = "Personnel",
|
title = "Personnel",
|
||||||
icon = Icons.AutoMirrored.Filled.EventNote,
|
icon = Icons.Default.LibraryMusic,
|
||||||
color = MaterialTheme.colorScheme.tertiary,
|
|
||||||
isExpanded = externalExpanded,
|
isExpanded = externalExpanded,
|
||||||
onToggle = { externalExpanded = !externalExpanded },
|
onToggle = { externalExpanded = !externalExpanded },
|
||||||
count = externalList.size
|
count = externalList.size
|
||||||
|
|
@ -170,12 +188,30 @@ fun SimpleDrawerContent(
|
||||||
if (externalExpanded) {
|
if (externalExpanded) {
|
||||||
items(externalList) { item ->
|
items(externalList) { item ->
|
||||||
val isSelected = item.path == activePath
|
val isSelected = item.path == activePath
|
||||||
|
val favoriteLists by sharedScreenModel.playlistItems.collectAsState()
|
||||||
|
val isFavorite = favoriteLists.contains(item)
|
||||||
|
|
||||||
if (item.path != "") {
|
if (item.path != "") {
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
icon = {
|
icon = {
|
||||||
Text("♬", fontSize = 18.sp)
|
Text("♬", fontSize = 20.sp)
|
||||||
|
},
|
||||||
|
label = { DrawerItemLabel(item) },
|
||||||
|
badge = {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
sharedScreenModel.toggleFavorite(item.path)
|
||||||
|
},
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.PlaylistAddCircle,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = if (!isFavorite) Color.LightGray else Color(0xFFFFD700),
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
label = { DrawerItemLabel(item, sharedScreenModel) },
|
|
||||||
selected = isSelected,
|
selected = isSelected,
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
|
|
@ -186,8 +222,8 @@ fun SimpleDrawerContent(
|
||||||
shape = RoundedCornerShape(5.dp),
|
shape = RoundedCornerShape(5.dp),
|
||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp),
|
modifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp),
|
||||||
colors = NavigationDrawerItemDefaults.colors(
|
colors = NavigationDrawerItemDefaults.colors(
|
||||||
selectedContainerColor = Color.Blue.copy(alpha = 0.1f),
|
selectedContainerColor = MaterialTheme.colorScheme.secondary.copy(alpha = 0.5f),
|
||||||
selectedTextColor = Color.Blue
|
selectedTextColor = MaterialTheme.colorScheme.onSecondary
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -197,8 +233,7 @@ fun SimpleDrawerContent(
|
||||||
stickyHeader {
|
stickyHeader {
|
||||||
DrawerHeaderSticky(
|
DrawerHeaderSticky(
|
||||||
title = "Playlist",
|
title = "Playlist",
|
||||||
icon = Icons.AutoMirrored.Filled.StarHalf,
|
icon = Icons.AutoMirrored.Filled.PlaylistPlay,
|
||||||
color = MaterialTheme.colorScheme.tertiary,
|
|
||||||
isExpanded = playListExpanded,
|
isExpanded = playListExpanded,
|
||||||
onToggle = { playListExpanded = !playListExpanded },
|
onToggle = { playListExpanded = !playListExpanded },
|
||||||
count = playList.size
|
count = playList.size
|
||||||
|
|
@ -211,8 +246,36 @@ fun SimpleDrawerContent(
|
||||||
val isSelected = item.path == activePath
|
val isSelected = item.path == activePath
|
||||||
|
|
||||||
NavigationDrawerItem(
|
NavigationDrawerItem(
|
||||||
|
icon = {
|
||||||
|
Text("♯", fontSize = 22.sp)
|
||||||
|
},
|
||||||
label = {
|
label = {
|
||||||
DrawerFavorisItemLabel(item, index, isSelected, sharedScreenModel)
|
DrawerItemLabel(item)
|
||||||
|
},
|
||||||
|
badge = {
|
||||||
|
if (index > 0) {
|
||||||
|
IconButton(
|
||||||
|
onClick = { sharedScreenModel.moveToTop(index) },
|
||||||
|
modifier = Modifier.size(30.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.VerticalAlignTop,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
tint = MaterialTheme.colorScheme.onSecondary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = { sharedScreenModel.toggleFavorite(item.path) },
|
||||||
|
modifier = Modifier.size(30.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Star,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = if (isSelected) MaterialTheme.colorScheme.background else MaterialTheme.colorScheme.tertiary
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
selected = isSelected,
|
selected = isSelected,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -221,11 +284,11 @@ fun SimpleDrawerContent(
|
||||||
solfaScreenModel.loadFromFile(item.path)
|
solfaScreenModel.loadFromFile(item.path)
|
||||||
onSongSelected(midi)
|
onSongSelected(midi)
|
||||||
},
|
},
|
||||||
|
shape = RoundedCornerShape(5.dp),
|
||||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp),
|
modifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp),
|
||||||
colors = NavigationDrawerItemDefaults.colors(
|
colors = NavigationDrawerItemDefaults.colors(
|
||||||
selectedContainerColor = Color(0xFFFFD700).copy(alpha = 0.3f),
|
selectedContainerColor = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.5f),
|
||||||
selectedTextColor = Color(0xFF665500),
|
selectedTextColor = MaterialTheme.colorScheme.onTertiary,
|
||||||
selectedIconColor = Color(0xFF665500)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -250,7 +313,7 @@ fun SimpleDrawerContent(
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Default.GraphicEq, contentDescription = null, tint = MaterialTheme.colorScheme.primary)
|
Icon(Icons.Default.GraphicEq, contentDescription = null, tint = MaterialTheme.colorScheme.onSecondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,7 +326,7 @@ fun SimpleDrawerContent(
|
||||||
solfaScreenModel.loadCustomFile()
|
solfaScreenModel.loadCustomFile()
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Default.FolderOpen, contentDescription = null)
|
Icon(Icons.Default.FolderOpen, contentDescription = null, tint = MaterialTheme.colorScheme.onSecondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,7 +339,7 @@ fun SimpleDrawerContent(
|
||||||
onSettingsCheck()
|
onSettingsCheck()
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Default.Settings, contentDescription = null)
|
Icon(Icons.Default.Settings, contentDescription = null, tint = MaterialTheme.colorScheme.onSecondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -288,7 +351,7 @@ fun SimpleDrawerContent(
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
scope.launch { drawerState.close() }
|
scope.launch { drawerState.close() }
|
||||||
}) {
|
}) {
|
||||||
Icon(Icons.Default.Info, contentDescription = null)
|
Icon(Icons.Default.Info, contentDescription = null, tint = MaterialTheme.colorScheme.onSecondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -300,7 +363,6 @@ fun SimpleDrawerContent(
|
||||||
fun DrawerHeaderSticky(
|
fun DrawerHeaderSticky(
|
||||||
title: String,
|
title: String,
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
color: Color,
|
|
||||||
isExpanded: Boolean,
|
isExpanded: Boolean,
|
||||||
onToggle: () -> Unit,
|
onToggle: () -> Unit,
|
||||||
count: Int
|
count: Int
|
||||||
|
|
@ -315,23 +377,23 @@ fun DrawerHeaderSticky(
|
||||||
modifier = Modifier.padding(16.dp),
|
modifier = Modifier.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
Icon(icon, contentDescription = null, tint = color, modifier = Modifier.size(20.dp))
|
Icon(icon, contentDescription = null, tint = MaterialTheme.colorScheme.primary, modifier = Modifier.size(20.dp))
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "$title".uppercase(),
|
text = title.uppercase(),
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
style = MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.Bold),
|
style = MaterialTheme.typography.titleSmall.copy(fontWeight = FontWeight.Bold),
|
||||||
color = color
|
color = MaterialTheme.colorScheme.primary
|
||||||
)
|
)
|
||||||
Surface(
|
Surface(
|
||||||
color = Color.Green.copy(alpha = 0.1f),
|
color = MaterialTheme.colorScheme.error.copy(alpha = 0.1f),
|
||||||
shape = MaterialTheme.shapes.extraSmall
|
shape = MaterialTheme.shapes.extraSmall
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "${count}",
|
text = count.toString() ,
|
||||||
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp),
|
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = Color.Green
|
color = MaterialTheme.colorScheme.error
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Icon(
|
Icon(
|
||||||
|
|
@ -344,94 +406,25 @@ fun DrawerHeaderSticky(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DrawerItemLabel(item: DrawerItem, sharedScreenModel: SharedScreenModel) {
|
fun DrawerItemLabel(item: DrawerItem) {
|
||||||
val favoriteLists by sharedScreenModel.playlistItems.collectAsState()
|
|
||||||
val isFavorite = favoriteLists.contains(item)
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text(text = item.title, fontWeight = FontWeight.SemiBold, style = MaterialTheme.typography.bodyMedium)
|
Text(text = item.title, fontWeight = FontWeight.SemiBold, style = MaterialTheme.typography.titleSmall)
|
||||||
Text(text = item.contentTitle, style = MaterialTheme.typography.labelSmall, maxLines = 1)
|
Text(text = item.contentTitle, style = MaterialTheme.typography.labelSmall, maxLines = 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
sharedScreenModel.toggleFavorite(item.path)
|
|
||||||
},
|
|
||||||
modifier = Modifier.size(32.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.PlaylistAddCircle,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = if (!isFavorite) Color.LightGray else Color(0xFFFFD700),
|
|
||||||
modifier = Modifier.size(18.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun DrawerFavorisItemLabel(item: DrawerItem, index: Int, isSelected: Boolean, sharedScreenModel: SharedScreenModel) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Reorder,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.padding(end = 8.dp).size(18.dp),
|
|
||||||
tint = Color.Gray.copy(alpha = 0.5f)
|
|
||||||
)
|
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
|
||||||
Text(
|
|
||||||
text = item.title,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
style = MaterialTheme.typography.bodyMedium
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = item.contentTitle,
|
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
maxLines = 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > 0) {
|
|
||||||
IconButton(
|
|
||||||
onClick = { sharedScreenModel.moveToTop(index) },
|
|
||||||
modifier = Modifier.size(30.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.VerticalAlignTop,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(16.dp),
|
|
||||||
tint = if (isSelected) Color.White else Color.Gray
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IconButton(
|
|
||||||
onClick = { sharedScreenModel.toggleFavorite(item.path) },
|
|
||||||
modifier = Modifier.size(30.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Star,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = if (isSelected) Color.White else Color(0xFFFFD700)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CustomSwitchItem(
|
fun CustomSwitchItem(
|
||||||
checked: Boolean,
|
checked: Boolean,
|
||||||
onCheckedChange: (Boolean) -> Unit,
|
onCheckedChange: (Boolean) -> Unit,
|
||||||
label: String,
|
label: String,
|
||||||
color: Color
|
thumbIcon: ImageVector
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
|
@ -440,11 +433,20 @@ fun CustomSwitchItem(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
onCheckedChange = onCheckedChange,
|
onCheckedChange = onCheckedChange,
|
||||||
colors = SwitchDefaults.colors(
|
colors = SwitchDefaults.colors(
|
||||||
checkedThumbColor = Color.White,
|
checkedThumbColor = MaterialTheme.colorScheme.error,
|
||||||
checkedTrackColor = color,
|
checkedTrackColor = MaterialTheme.colorScheme.error.copy(alpha = 0.5f),
|
||||||
uncheckedTrackColor = color.copy(alpha = 0.3f)
|
uncheckedThumbColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.4f),
|
||||||
|
uncheckedTrackColor = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.1f),
|
||||||
|
uncheckedBorderColor = MaterialTheme.colorScheme.error.copy(alpha = 0.2f),
|
||||||
),
|
),
|
||||||
modifier = Modifier.scale(0.7f)
|
modifier = Modifier.scale(0.8f),
|
||||||
|
thumbContent = {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (checked) thumbIcon else Icons.Default.Visibility,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(SwitchDefaults.IconSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = label,
|
text = label,
|
||||||
|
|
|
||||||
161
composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/Theme.kt
Normal file
161
composeApp/src/commonMain/kotlin/mg/dot/feufaro/ui/Theme.kt
Normal file
|
|
@ -0,0 +1,161 @@
|
||||||
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
|
import androidx.compose.material3.darkColorScheme
|
||||||
|
import androidx.compose.material3.lightColorScheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
|
object ThemeDefaults {
|
||||||
|
val PrimaryBlue = Color(0xFF5061FF)
|
||||||
|
val AccentYellow = Color(0xFFFFFA54)
|
||||||
|
val AccentGreen = Color(0xFF3EFF96)
|
||||||
|
val AccentPink = Color(0xFFFF4FA8)
|
||||||
|
|
||||||
|
val LightBackground = Color(0xFFF9F9FB)
|
||||||
|
val DarkBackground = Color(0xFF121214)
|
||||||
|
|
||||||
|
|
||||||
|
fun getSelectedThemeColors(themeModeStr: String): Pair<ColorScheme, ColorScheme> {
|
||||||
|
return when (themeModeStr.uppercase()) {
|
||||||
|
"SYSTEM", "DARK", "LIGHT" -> Pair(
|
||||||
|
lightColorScheme(
|
||||||
|
primary = PrimaryBlue.copy(alpha = 0.85f),
|
||||||
|
onPrimary = LightBackground,
|
||||||
|
|
||||||
|
secondary = AccentGreen.copy(alpha = 0.85f),
|
||||||
|
onSecondary = DarkBackground,
|
||||||
|
secondaryContainer = AccentGreen.copy(alpha = 0.85f),
|
||||||
|
onSecondaryContainer = DarkBackground,
|
||||||
|
|
||||||
|
tertiary = AccentYellow.copy(alpha = 0.85f),
|
||||||
|
onTertiary = DarkBackground,
|
||||||
|
|
||||||
|
error = AccentPink.copy(alpha = 0.85f),
|
||||||
|
onError = LightBackground,
|
||||||
|
|
||||||
|
background = LightBackground,
|
||||||
|
surface = LightBackground
|
||||||
|
),
|
||||||
|
darkColorScheme(
|
||||||
|
primary = PrimaryBlue,
|
||||||
|
onPrimary = LightBackground,
|
||||||
|
|
||||||
|
secondary = AccentGreen,
|
||||||
|
onSecondary = LightBackground,
|
||||||
|
secondaryContainer = AccentGreen,
|
||||||
|
onSecondaryContainer = DarkBackground,
|
||||||
|
|
||||||
|
tertiary = AccentYellow,
|
||||||
|
onTertiary = DarkBackground,
|
||||||
|
|
||||||
|
error = AccentPink,
|
||||||
|
onError = LightBackground,
|
||||||
|
|
||||||
|
background = DarkBackground,
|
||||||
|
surface = Color(0xFF1E1E24),
|
||||||
|
onBackground = LightBackground,
|
||||||
|
onSurface = LightBackground
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else -> Pair(
|
||||||
|
lightColorScheme(primary = PrimaryBlue, background = LightBackground),
|
||||||
|
darkColorScheme(primary = PrimaryBlue, background = DarkBackground)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FeufaroTheme(
|
||||||
|
themeModeSelected: String,
|
||||||
|
userFontSize: Float,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val (lightScheme, darkScheme) = ThemeDefaults.getSelectedThemeColors(themeModeSelected)
|
||||||
|
val useDarkTheme = when (themeModeSelected.uppercase()) {
|
||||||
|
"DARK" -> true
|
||||||
|
"LIGHT" -> false
|
||||||
|
else -> isSystemInDarkTheme()
|
||||||
|
}
|
||||||
|
|
||||||
|
val colorScheme = if (useDarkTheme) darkScheme else lightScheme
|
||||||
|
|
||||||
|
val baseSize = userFontSize
|
||||||
|
|
||||||
|
val customTypography = Typography(
|
||||||
|
displayLarge = TextStyle(
|
||||||
|
fontSize = (baseSize * 2.4f).sp,
|
||||||
|
letterSpacing = (-1).sp
|
||||||
|
),
|
||||||
|
displayMedium = TextStyle(
|
||||||
|
fontSize = (baseSize * 2.0f).sp,
|
||||||
|
letterSpacing = (-0.5).sp
|
||||||
|
),
|
||||||
|
displaySmall = TextStyle(
|
||||||
|
fontSize = (baseSize * 1.7f).sp
|
||||||
|
),
|
||||||
|
|
||||||
|
headlineLarge = TextStyle(
|
||||||
|
fontSize = (baseSize * 1.5f).sp,
|
||||||
|
letterSpacing = 0.sp
|
||||||
|
),
|
||||||
|
headlineMedium = TextStyle(
|
||||||
|
fontSize = (baseSize * 1.35f).sp
|
||||||
|
),
|
||||||
|
headlineSmall = TextStyle(
|
||||||
|
fontSize = (baseSize * 1.2f).sp
|
||||||
|
),
|
||||||
|
|
||||||
|
titleLarge = TextStyle(
|
||||||
|
fontSize = (baseSize + 4f).sp,
|
||||||
|
letterSpacing = 0.15.sp
|
||||||
|
),
|
||||||
|
titleMedium = TextStyle(
|
||||||
|
fontSize = (baseSize + 2f).sp,
|
||||||
|
letterSpacing = 0.1.sp
|
||||||
|
),
|
||||||
|
titleSmall = TextStyle(
|
||||||
|
fontSize = baseSize.sp,
|
||||||
|
letterSpacing = 0.1.sp
|
||||||
|
),
|
||||||
|
|
||||||
|
bodyLarge = TextStyle(
|
||||||
|
fontSize = baseSize.sp,
|
||||||
|
letterSpacing = 0.25.sp
|
||||||
|
),
|
||||||
|
bodyMedium = TextStyle(
|
||||||
|
fontSize = (baseSize - 1.5f).sp,
|
||||||
|
letterSpacing = 0.25.sp
|
||||||
|
),
|
||||||
|
bodySmall = TextStyle(
|
||||||
|
fontSize = (baseSize - 3f).sp,
|
||||||
|
letterSpacing = 0.4.sp
|
||||||
|
),
|
||||||
|
|
||||||
|
labelLarge = TextStyle(
|
||||||
|
fontSize = (baseSize - 0.5f).sp,
|
||||||
|
letterSpacing = 0.1.sp
|
||||||
|
),
|
||||||
|
labelMedium = TextStyle(
|
||||||
|
fontSize = (baseSize - 2.5f).sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
|
),
|
||||||
|
labelSmall = TextStyle(
|
||||||
|
fontSize = (baseSize - 4f).sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
MaterialTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = customTypography,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
package mg.dot.feufaro.ui
|
package mg.dot.feufaro.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.ScrollState
|
import androidx.compose.foundation.ScrollState
|
||||||
|
import androidx.compose.foundation.ScrollbarStyle
|
||||||
import androidx.compose.foundation.VerticalScrollbar
|
import androidx.compose.foundation.VerticalScrollbar
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.rememberScrollbarAdapter
|
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.rememberScrollbarAdapter
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
actual fun MyVerticalScrollbar (
|
actual fun MyVerticalScrollbar (
|
||||||
|
|
@ -29,10 +31,22 @@ actual fun MyVerticalScrollbar (
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val scrollbarUnhoverColor = MaterialTheme.colorScheme.secondary.copy(alpha = 0.85f)
|
||||||
|
val scrollbarHoverColor = MaterialTheme.colorScheme.secondary
|
||||||
|
val scrollbarBackground = MaterialTheme.colorScheme.onSecondary.copy(alpha = 0.25f)
|
||||||
|
|
||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
VerticalScrollbar(
|
VerticalScrollbar(
|
||||||
adapter = adapter,
|
adapter = adapter,
|
||||||
modifier = modifier.fillMaxHeight(0.5f).background(Color.Green.copy(0.75f))
|
modifier = modifier.fillMaxHeight(0.5f).background(scrollbarBackground),
|
||||||
|
style = ScrollbarStyle(
|
||||||
|
minimalHeight = 16.dp,
|
||||||
|
thickness = 8.dp,
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
hoverDurationMillis = 250,
|
||||||
|
unhoverColor = scrollbarUnhoverColor,
|
||||||
|
hoverColor = scrollbarHoverColor
|
||||||
|
)
|
||||||
)
|
)
|
||||||
//content()
|
//content()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue