Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Support for switching themes #121

Merged
merged 2 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.ExperimentalComposeLibrary
import java.net.URI

repositories {
mavenCentral()
maven { url = URI("https://jitpack.io") }
}

plugins {
alias(libs.plugins.kotlinMultiplatform)
Expand Down Expand Up @@ -38,6 +44,7 @@ kotlin {
implementation("io.insert-koin:koin-core:3.5.0")
implementation("com.github.kwhat:jnativehook:2.2.2")
implementation("app.cash.sqldelight:sqlite-driver:2.0.0")
implementation("com.github.Dansoftowner:jSystemThemeDetector:3.8")
}
commonMain.dependencies {
implementation(compose.runtime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import androidx.compose.ui.graphics.toComposeImageBitmap
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.dp
import com.clipevery.ui.AboutUI
import com.clipevery.ui.ClipeveryTheme
import com.clipevery.ui.ClipeveryTheme
import com.clipevery.ui.HomeUI
import com.clipevery.ui.SettingsUI
import org.koin.core.KoinApplication
Expand All @@ -38,7 +40,7 @@ fun ClipeveryApp(koinApplication: KoinApplication, hideWindow: () -> Unit) {

@Composable
fun ClipeveryWindow(hideWindow: () -> Unit) {
MaterialTheme {
ClipeveryTheme {
Box(modifier = Modifier
.background(Color.Transparent)
.pointerInput(Unit) {
Expand Down Expand Up @@ -77,7 +79,7 @@ fun ClipeveryWindow(hideWindow: () -> Unit) {
) {
Column(Modifier
.clip(RoundedCornerShape(10.dp))
.background(Color.White)
.background(MaterialTheme.colors.background)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally) {
ClipeveryContent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ import java.util.Locale
@Serializable
data class AppConfig(
val bindingState: Boolean = false,
val language: String = Locale.getDefault().language,)
val language: String = Locale.getDefault().language,
val isFollowSystem: Boolean = true,
val isDark: Boolean = false
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.clipevery.ui

import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import com.clipevery.LocalKoinApplication


private val LightColorPalette = lightColors(
primary = Color(0xFF1672FF),
primaryVariant = Color(0xFF247CFF),
secondaryVariant = Color(0xFFE8F5FF),
)

private val DarkColorPalette = darkColors(
primary = Color(0xFFBB86FC),
primaryVariant = Color(0xFF3700B3),
secondary = Color(0xFF363B3E),
secondaryVariant = Color(0xFF363B3E),
background = Color(0xFF202326),
surface = Color(0xFF2F3338)
)

@Composable
fun ClipeveryTheme(content: @Composable () -> Unit) {
val current = LocalKoinApplication.current
val themeDetector = current.koin.get<ThemeDetector>()

val colors = if (themeDetector.isFollowSystem()) {
if (themeDetector.isSystemInDark()) {
DarkColorPalette
} else {
LightColorPalette
}
} else {
if (themeDetector.isUserInDark()) {
DarkColorPalette
} else {
LightColorPalette
}
}
MaterialTheme(
colors = colors,
content = content
)
}

interface ThemeDetector {
fun isSystemInDark(): Boolean

fun isFollowSystem(): Boolean

fun isUserInDark(): Boolean

fun isCurrentThemeDark(): Boolean {
return isFollowSystem() && isSystemInDark() || !isFollowSystem() && isUserInDark()
}

fun setThemeConfig(isFollowSystem: Boolean, isUserInDark: Boolean = false)
}
6 changes: 4 additions & 2 deletions composeApp/src/commonMain/kotlin/com/clipevery/ui/HomeUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
Expand Down Expand Up @@ -187,7 +188,7 @@ fun TitleUI(currentPage: MutableState<PageType>) {
.width(180.dp)
.wrapContentHeight()
.clip(RoundedCornerShape(5.dp))
.background(Color.White)
.background(MaterialTheme.colors.surface)
) {
MenuItem(copywriter.getText("Check_for_updates")) {
// TODO: check for updates
Expand Down Expand Up @@ -223,10 +224,11 @@ fun TitleUI(currentPage: MutableState<PageType>) {
fun MenuItem(text: String, onClick: () -> Unit) {
val interactionSource = remember { MutableInteractionSource() }
val isHovered by interactionSource.collectIsHoveredAsState()
val backgroundColor = if (isHovered) Color(0xFFE8F5FF) else Color.Transparent
val backgroundColor = if (isHovered) MaterialTheme.colors.secondaryVariant else Color.Transparent

Text(
text = text,
color = MaterialTheme.colors.onBackground,
fontSize = 12.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand All @@ -20,7 +21,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
Expand All @@ -39,7 +39,7 @@ fun SettingsItemUI(title: String,
val current = LocalKoinApplication.current
val copywriter = current.koin.get<GlobalCopywriter>()
var hover by remember { mutableStateOf(false) }
val backgroundColor = if (hover) Color(0xFFE8F5FF) else Color.White
val backgroundColor = if (hover) MaterialTheme.colors.secondaryVariant else MaterialTheme.colors.background

var openSettings by remember { mutableStateOf(false) }

Expand Down Expand Up @@ -80,10 +80,10 @@ fun SettingsItemUI(title: String,
.size(15.dp),
imageVector = languageArrow,
contentDescription = null,
tint = Color.Gray
)
Text(
text = copywriter.getText(title),
tint = MaterialTheme.colors.onBackground
)
Text(text = copywriter.getText(title),
color = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light)
Expand Down
21 changes: 13 additions & 8 deletions composeApp/src/commonMain/kotlin/com/clipevery/ui/SettingsUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Switch
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -95,6 +96,7 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {
Row(modifier = Modifier.fillMaxWidth().padding(25.dp, 5.dp, 0.dp, 5.dp),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "${copywriter.getText("Language")}:",
color = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light))
Expand All @@ -113,8 +115,8 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {
languageSize = coordinates.size.toSize()
},
verticalAlignment = Alignment.CenterVertically) {
Text(
text = copywriter.getText("CurrentLanguage"),
Text(text = copywriter.getText("CurrentLanguage"),
color = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light)
Expand All @@ -125,7 +127,7 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {
.size(15.dp),
imageVector = languageArrow,
contentDescription = null,
tint = Color.Black
tint = MaterialTheme.colors.onBackground
)
}

Expand All @@ -151,7 +153,7 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {
.width(180.dp)
.wrapContentHeight()
.clip(RoundedCornerShape(5.dp))
.background(Color.White)
.background(MaterialTheme.colors.surface)
) {
val allLanguages = copywriter.getAllLanguages()
allLanguages.forEachIndexed { _, language ->
Expand All @@ -169,6 +171,7 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {
Row(modifier = Modifier.fillMaxWidth().padding(25.dp, 5.dp, 0.dp, 5.dp),
verticalAlignment = Alignment.CenterVertically) {
Text(text = "${copywriter.getText("Theme")}:",
color = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light))
Expand All @@ -187,6 +190,7 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {
)

Text(text = copywriter.getText("Boot_start_up"),
color = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light))
Expand All @@ -202,6 +206,7 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {
)

Text(text = copywriter.getText("AutomaticUpdate"),
color = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light))
Expand All @@ -211,8 +216,8 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {

SettingsItemUI("Network") {
Row(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
Text(
text = copywriter.getText("Network"),
Text(text = copywriter.getText("Network"),
color = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light)
Expand All @@ -222,8 +227,8 @@ fun SettingsContentUI(currentPage: MutableState<PageType>) {
Spacer(modifier = Modifier.height(10.dp))
SettingsItemUI("Store") {
Row(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
Text(
text = copywriter.getText("Store"),
Text(text = copywriter.getText("Store"),
color = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light)
Expand Down
4 changes: 3 additions & 1 deletion composeApp/src/commonMain/kotlin/com/clipevery/ui/TabsUI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.GenericShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Tab
import androidx.compose.material.TabRow
import androidx.compose.material.Text
Expand Down Expand Up @@ -95,7 +96,7 @@ fun TabUI(isSelect: Boolean, title: String, clickable: () -> Unit) {

if (isSelect) {
textStyle = TextStyle(fontWeight = FontWeight.Bold)
modifier = modifier.border(5.dp, Color(0xFF70b0f3), bottomBorderShape)
modifier = modifier.border(5.dp, MaterialTheme.colors.primary, bottomBorderShape)
textUnit = 16.sp
} else {
textStyle = TextStyle(fontWeight = FontWeight.Normal)
Expand All @@ -105,6 +106,7 @@ fun TabUI(isSelect: Boolean, title: String, clickable: () -> Unit) {
Box(modifier = modifier) {
Text(
text = title,
color = MaterialTheme.colors.onBackground,
fontSize = textUnit,
style = textStyle,
fontFamily = FontFamily.SansSerif,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
Expand All @@ -27,17 +25,17 @@ import com.clipevery.i18n.GlobalCopywriter
fun ThemeSegmentedControl() {
val current = LocalKoinApplication.current
val copywriter = current.koin.get<GlobalCopywriter>()
val selectedOption = remember { mutableStateOf("System") }
val themeDetector = current.koin.get<ThemeDetector>()

Row(verticalAlignment = Alignment.CenterVertically) {
// Light Button
Button(
modifier = Modifier.height(28.dp),
onClick = { selectedOption.value = "Light" },
onClick = { themeDetector.setThemeConfig(isFollowSystem = false, isUserInDark = false) },
// Apply the shape only to the left side for the first button
shape = RoundedCornerShape(topStart = 4.dp, bottomStart = 4.dp, topEnd = 0.dp, bottomEnd = 0.dp),
// Change the background and content colors based on selection
colors = if (selectedOption.value == "Light") ButtonDefaults.buttonColors(backgroundColor = Color(0xff1672ff))
colors = if (!themeDetector.isFollowSystem() && !themeDetector.isCurrentThemeDark()) ButtonDefaults.buttonColors(backgroundColor = Color(0xff1672ff))
else ButtonDefaults.buttonColors(backgroundColor = Color.White),
border = BorderStroke(1.dp, Color(0xFFAFCBE1)),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
Expand All @@ -47,16 +45,16 @@ fun ThemeSegmentedControl() {
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light),
color = if (selectedOption.value == "Light") Color.White else Color.Black)
color = if (!themeDetector.isFollowSystem() && !themeDetector.isCurrentThemeDark()) Color.White else Color.Black)
}

// System Button
Button(
modifier = Modifier.height(28.dp).offset(x = (-1).dp),
onClick = { selectedOption.value = "System" },
onClick = { themeDetector.setThemeConfig(isFollowSystem = true) },
// No shape for the middle button to keep it rectangular
shape = RoundedCornerShape(0.dp),
colors = if (selectedOption.value == "System") ButtonDefaults.buttonColors(backgroundColor = Color(0xff1672ff))
colors = if (themeDetector.isFollowSystem()) ButtonDefaults.buttonColors(backgroundColor = Color(0xff1672ff))
else ButtonDefaults.buttonColors(backgroundColor = Color.White),
border = BorderStroke(1.dp, Color(0xFFAFCBE1)),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
Expand All @@ -66,16 +64,16 @@ fun ThemeSegmentedControl() {
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light),
color = if (selectedOption.value == "System") Color.White else Color.Black)
color = if (themeDetector.isFollowSystem()) Color.White else Color.Black)
}

// Dark Button
Button(
modifier = Modifier.height(28.dp).offset(x = (-2).dp),
onClick = { selectedOption.value = "Dark" },
onClick = { themeDetector.setThemeConfig(isFollowSystem = false, isUserInDark = true) },
// Apply the shape only to the right side for the last button
shape = RoundedCornerShape(topStart = 0.dp, bottomStart = 0.dp, topEnd = 4.dp, bottomEnd = 4.dp),
colors = if (selectedOption.value == "Dark") ButtonDefaults.buttonColors(backgroundColor = Color(0xff1672ff))
colors = if (!themeDetector.isFollowSystem() && themeDetector.isCurrentThemeDark()) ButtonDefaults.buttonColors(backgroundColor = Color(0xff1672ff))
else ButtonDefaults.buttonColors(backgroundColor = Color.White),
border = BorderStroke(1.dp, Color(0xFFAFCBE1)),
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
Expand All @@ -85,7 +83,7 @@ fun ThemeSegmentedControl() {
fontSize = 14.sp,
fontFamily = FontFamily.SansSerif,
style = TextStyle(fontWeight = FontWeight.Light),
color = if (selectedOption.value == "Dark") Color.White else Color.Black)
color = if (!themeDetector.isFollowSystem() && themeDetector.isCurrentThemeDark()) Color.White else Color.Black)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,10 @@ fun DecorationUI(currentPage: MutableState<PageType>, title: String) {
.clickable { currentPage.value = PageType.HOME }) {
Icon(imageVector = arrowLeft(),
contentDescription = null,
tint = Color(0xFF70b0f3))
tint = Color(0xFF1672FF))
Text(
text = copywriter.getText("Return"),
color = Color(0xFF70b0f3),
color = Color(0xFF1672FF),
fontWeight = FontWeight.Light
)
}
Expand Down
Loading
Loading