feature: Rework settings (#200)

Co-authored-by: TEYSSANDIER Raphael <rteyssandier@sephora.fr>
This commit is contained in:
Raphael Teyssandier 2025-09-04 11:15:25 +02:00 committed by GitHub
parent 05185a712a
commit 58b836f8a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 135 additions and 160 deletions

View file

@ -17,5 +17,6 @@ expect fun FloconWindow(
title: String,
state: FloconWindowState,
onCloseRequest: () -> Unit,
content: @Composable () -> Unit,
alwaysOnTop: Boolean = false,
content: @Composable () -> Unit
)

View file

@ -4,9 +4,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import com.mikepenz.aboutlibraries.ui.compose.m3.chipColors
import com.mikepenz.aboutlibraries.ui.compose.m3.libraryColors
import com.mikepenz.aboutlibraries.ui.compose.rememberLibraries
import flocondesktop.composeapp.generated.resources.Res
import io.github.openflocon.library.designsystem.FloconTheme
import kotlinx.collections.immutable.toImmutableList
@Composable
@ -16,8 +20,26 @@ fun AboutScreen(modifier: Modifier = Modifier) {
// Replace with your specific resource loading logic
Res.readBytes("files/aboutlibraries.json").decodeToString()
}
LibrariesContainer(
libraries = libraries?.distinct(),
colors = LibraryDefaults.libraryColors(
backgroundColor = FloconTheme.colorPalette.surface,
contentColor = FloconTheme.colorPalette.onSurface,
versionChipColors = LibraryDefaults.chipColors(
containerColor = FloconTheme.colorPalette.primary,
contentColor = FloconTheme.colorPalette.onPrimary
),
licenseChipColors = LibraryDefaults.chipColors(
containerColor = FloconTheme.colorPalette.secondary,
contentColor = FloconTheme.colorPalette.onSecondary
),
fundingChipColors = LibraryDefaults.chipColors(
containerColor = FloconTheme.colorPalette.tertiary,
contentColor = FloconTheme.colorPalette.onTertiary
),
dialogConfirmButtonColor = FloconTheme.colorPalette.onSurface
),
modifier = modifier,
)
}

View file

@ -1,7 +1,6 @@
package io.github.openflocon.flocondesktop.main.ui.settings
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -9,7 +8,9 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Check
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@ -20,14 +21,14 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import co.touchlab.kermit.Logger
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.FloconSurface
import io.github.openflocon.library.designsystem.components.FloconTextField
import io.github.openflocon.library.designsystem.components.FloconButton
import io.github.openflocon.library.designsystem.components.FloconFeature
import io.github.openflocon.library.designsystem.components.FloconIcon
import io.github.openflocon.library.designsystem.components.FloconTextFieldWithoutM3
import io.github.openflocon.library.designsystem.components.defaultPlaceHolder
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.koin.compose.viewmodel.koinViewModel
@ -36,10 +37,8 @@ fun SettingsScreen(
modifier: Modifier = Modifier,
) {
val viewModel: SettingsViewModel = koinViewModel()
val needsAdbSetup by viewModel.needsAdbSetup.collectAsState()
val adbPathText by viewModel.adbPathInput.collectAsState()
val displayAboutScreen by viewModel.displayAboutScreen.collectAsState()
Box(modifier = modifier) {
SettingsScreen(
@ -48,22 +47,8 @@ fun SettingsScreen(
onAdbPathChanged = viewModel::onAdbPathChanged,
saveAdbPath = viewModel::saveAdbPath,
testAdbPath = viewModel::testAdbPath,
onClickLicenses = viewModel::displayAboutScreen,
needsAdbSetup = needsAdbSetup,
)
if (displayAboutScreen) {
Dialog(
onDismissRequest = {
viewModel.hideAboutScreen()
},
) {
AboutScreen(
modifier = Modifier
.fillMaxSize()
.background(FloconTheme.colorPalette.primary),
)
}
}
}
}
@ -74,102 +59,61 @@ private fun SettingsScreen(
onAdbPathChanged: (String) -> Unit,
saveAdbPath: () -> Unit,
testAdbPath: () -> Unit,
onClickLicenses: () -> Unit,
needsAdbSetup: Boolean,
modifier: Modifier = Modifier,
) {
FloconSurface(modifier = modifier) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier.fillMaxWidth()
.background(FloconTheme.colorPalette.primary)
.padding(all = 12.dp),
) {
FloconFeature(
modifier = modifier.fillMaxSize()
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.clip(FloconTheme.shapes.medium)
.background(FloconTheme.colorPalette.primary)
.padding(all = 8.dp)
) {
if (needsAdbSetup) {
Text(
text = "Settings",
style = FloconTheme.typography.titleLarge,
color = FloconTheme.colorPalette.onSurface,
text = "Please setup ADB first, this field is mandatory",
color = FloconTheme.colorPalette.onError,
style = FloconTheme.typography.bodySmall,
)
SettingsButton(
shape = RoundedCornerShape(12.dp),
modifier = Modifier.align(Alignment.CenterEnd),
onClick = onClickLicenses,
text = "Licenses",
)
}
Column(modifier = Modifier.fillMaxSize().padding(all = 16.dp)) {
Column(
modifier = Modifier
.clip(RoundedCornerShape(12.dp))
.background(FloconTheme.colorPalette.primary)
.padding(all = 12.dp),
} else {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
if (needsAdbSetup) {
Text(
modifier = Modifier
.padding(bottom = 4.dp)
.padding(horizontal = 8.dp),
text = "Please setup ADB first, this field is mandatory",
color = Color.Red,
style = FloconTheme.typography.bodySmall,
)
} else {
Text(
modifier = Modifier
.padding(bottom = 4.dp)
.padding(horizontal = 8.dp),
text = "ADB configuraton is valid",
color = Color.Green,
style = FloconTheme.typography.bodySmall,
)
}
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Box(
modifier = Modifier
.weight(1f)
.background(
color = Color.White.copy(alpha = 0.1f),
shape = RoundedCornerShape(8.dp),
).padding(horizontal = 12.dp, vertical = 8.dp),
) {
if (adbPathText.isEmpty())
Text(
text = "Ex: /Users/youruser/Library/Android/sdk/platform-tools/adb",
style = FloconTheme.typography.bodySmall,
color = FloconTheme.colorPalette.onSurface.copy(alpha = 0.45f),
)
FloconTextField(
value = adbPathText,
onValueChange = onAdbPathChanged,
modifier = Modifier.fillMaxWidth()
)
}
SettingsButton(
text = "Save",
onClick = {
saveAdbPath()
},
)
}
Row(
modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
horizontalArrangement = Arrangement.End, // Align buttons to the end
verticalAlignment = Alignment.CenterVertically,
) {
SettingsButton(
onClick = { testAdbPath() },
text = "Test ADB",
)
}
FloconIcon(
imageVector = Icons.Outlined.Check,
tint = FloconTheme.colorPalette.onAccent,
modifier = Modifier.size(16.dp)
)
Text(
text = "ADB configuraton is valid",
color = FloconTheme.colorPalette.onAccent,
style = FloconTheme.typography.bodySmall
)
}
}
FloconTextFieldWithoutM3(
value = adbPathText,
onValueChange = onAdbPathChanged,
placeholder = defaultPlaceHolder("Eg: /Users/youruser/Library/Android/sdk/platform-tools/adb"),
containerColor = FloconTheme.colorPalette.secondary,
modifier = Modifier.fillMaxWidth()
)
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
SettingsButton(
text = "Save",
onClick = saveAdbPath
)
SettingsButton(
onClick = testAdbPath,
text = "Test",
)
}
}
}
}
@ -178,21 +122,16 @@ private fun SettingsScreen(
private fun SettingsButton(
onClick: () -> Unit,
text: String,
shape: Shape = RoundedCornerShape(4.dp),
modifier: Modifier = Modifier,
) {
Box(
FloconButton(
onClick = onClick,
containerColor = FloconTheme.colorPalette.secondary,
modifier = modifier
.clip(shape)
.background(Color.White)
.clickable(onClick = onClick)
.padding(horizontal = 8.dp, vertical = 6.dp),
) {
Text(
text = text,
style = FloconTheme.typography.bodySmall.copy(
color = Color.Black,
),
style = FloconTheme.typography.bodySmall
)
}
}
@ -213,7 +152,6 @@ private fun SettingsScreenPreview() {
},
modifier = Modifier.fillMaxSize(),
needsAdbSetup = false,
onClickLicenses = {},
)
}
}
@ -230,7 +168,6 @@ private fun SettingsScreenPreview_needsAdbSetup() {
testAdbPath = { Logger.d { "Test ADB FilePathDomainModel: $adbPath" } },
modifier = Modifier.fillMaxSize(),
needsAdbSetup = true,
onClickLicenses = {},
)
}
}

View file

@ -21,8 +21,6 @@ class SettingsViewModel(
private val _adbPathInput = MutableStateFlow("")
val adbPathInput = _adbPathInput.asStateFlow()
private val _displayAboutScreen = MutableStateFlow(false)
val displayAboutScreen = _displayAboutScreen.asStateFlow()
val needsAdbSetup = initialSetupStateHolder.needsAdbSetup
init {
@ -34,14 +32,6 @@ class SettingsViewModel(
}
}
fun displayAboutScreen() {
_displayAboutScreen.value = true
}
fun hideAboutScreen() {
_displayAboutScreen.value = false
}
fun onAdbPathChanged(newPath: String) {
_adbPathInput.value = newPath
}

View file

@ -3,7 +3,6 @@ package io.github.openflocon.flocondesktop.common.ui.window
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPlacement
import androidx.compose.ui.window.WindowPosition
@ -31,12 +30,14 @@ actual fun FloconWindow(
title: String,
state: FloconWindowState,
onCloseRequest: () -> Unit,
alwaysOnTop: Boolean,
content: @Composable () -> Unit,
) {
Window(
title = title,
icon = painterResource(Res.drawable.app_icon),
state = (state as FloconWindowStateDesktop).windowState,
alwaysOnTop = alwaysOnTop,
onCloseRequest = onCloseRequest,
) {
content()

View file

@ -1,11 +1,14 @@
package io.github.openflocon.flocondesktop
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.ApplicationScope
import androidx.compose.ui.window.FrameWindowScope
import androidx.compose.ui.window.MenuBar
@ -23,12 +26,15 @@ import flocondesktop.composeapp.generated.resources.app_icon_small
import io.github.openflocon.domain.feedback.FeedbackDisplayer
import io.github.openflocon.domain.feedback.FeedbackDisplayerHandler
import io.github.openflocon.flocondesktop.about.AboutScreen
import io.github.openflocon.flocondesktop.common.ui.window.FloconWindow
import io.github.openflocon.flocondesktop.common.ui.window.createFloconWindowState
import io.github.openflocon.flocondesktop.window.MIN_WINDOW_HEIGHT
import io.github.openflocon.flocondesktop.window.MIN_WINDOW_WIDTH
import io.github.openflocon.flocondesktop.window.WindowStateData
import io.github.openflocon.flocondesktop.window.WindowStateSaver
import io.github.openflocon.flocondesktop.window.size
import io.github.openflocon.flocondesktop.window.windowPosition
import io.github.openflocon.library.designsystem.FloconTheme
import org.jetbrains.compose.resources.painterResource
import org.koin.compose.koinInject
import java.awt.Desktop
@ -86,8 +92,7 @@ fun main() {
if (ACTIVATE_TRAY_NOTIFICATION) {
FloconTray()
}
// TODO later
// FloconMenu()
FloconMenu()
if (openAbout) {
AboutScreen(
@ -100,27 +105,35 @@ fun main() {
@Composable
private fun FrameWindowScope.FloconMenu() {
var openSettings by remember { mutableStateOf(false) }
var openLicenses by remember { mutableStateOf(false) }
MenuBar {
Menu(
text = "Settings"
) {
Item(
text = "Open",
text = "Licences",
onClick = {
openSettings = true
openLicenses = true
}
)
}
}
// TODO Later
// if (openSettings) {
// SettingsScreen(
// onCloseRequest = { openSettings = false }
// )
// }
if (openLicenses) {
FloconWindow(
title = "Licenses",
state = createFloconWindowState(),
alwaysOnTop = true,
onCloseRequest = { openLicenses = false },
) {
io.github.openflocon.flocondesktop.main.ui.settings.AboutScreen(
modifier = Modifier
.fillMaxSize()
.background(FloconTheme.colorPalette.primary),
)
}
}
}
@Composable