Organise themes

This commit is contained in:
Andrew 2024-06-13 10:33:15 +09:30
parent 9eb7316c20
commit f00d2ff06d
6 changed files with 154 additions and 162 deletions

View File

@ -9,15 +9,21 @@ import androidx.compose.material.Colors
import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButtonDefaults
import androidx.compose.material.TabRowDefaults
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.primarySurface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import org.session.libsession.utilities.AppTextSecurePreferences
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.util.ThemeState
import org.thoughtcrime.securesms.util.themeState
const val classicDark0 = 0xff111111
const val classicDark1 = 0xff1B1B1B
@ -67,6 +73,82 @@ val classicDarkColors = classicDarks.map(::Color)
val blackAlpha40 = Color.Black.copy(alpha = 0.4f)
val LocalColors = staticCompositionLocalOf { sessionColors(isLight = false, isClassic = true) }
data class SessionColors(
val isLight: Boolean = false,
val primary: Color = Color.Unspecified,
val danger: Color = Color.Unspecified,
val disabled: Color = Color.Unspecified,
val background: Color = Color.Unspecified,
val backgroundSecondary: Color = Color.Unspecified,
val text: Color = Color.Unspecified,
val textSecondary: Color = Color.Unspecified,
val borders: Color = Color.Unspecified,
val textBubbleSent: Color = Color.Unspecified,
val backgroundBubbleReceived: Color = Color.Unspecified,
val textBubbleReceived: Color = Color.Unspecified,
) {
val backgroundLight get() = if (isLight) backgroundSecondary else Color.White
val onBackgroundLight get() = if (isLight) text else background
val button get() = if (isLight) text else primary
val divider get() = text.copy(alpha = TabRowDefaults.DividerOpacity)
val backgroundBubbleSent get() = primary
@Composable fun radioButtonColors() = RadioButtonDefaults.colors(selectedColor = primary, unselectedColor = text, disabledColor = disabled)
}
val primaryGreen = Color(0xFF31F196)
val primaryBlue = Color(0xFF57C9FA)
val primaryPurple = Color(0xFFC993FF)
val primaryPink = Color(0xFFFF95EF)
val primaryRed = Color(0xFFFF9C8E)
val primaryOrange = Color(0xFFFCB159)
val primaryYellow = Color(0xFFFAD657)
val dangerDark = Color(0xFFFF3A3A)
val dangerLight = Color(0xFFE12D19)
val disabledDark = Color(0xFFA1A2A1)
val disabledLioht = Color(0xFF6D6D6D)
val primaryColors = listOf(
primaryGreen,
primaryBlue,
primaryPurple,
primaryPink,
primaryRed,
primaryOrange,
primaryYellow,
)
private class UnresolvedColor(val function: (Boolean, Boolean) -> Color) {
operator fun invoke(isLight: Boolean, isClassic: Boolean) = function(isLight, isClassic)
constructor(light: Color, dark: Color): this(function = { isLight, _ -> if (isLight) light else dark })
constructor(classicDark: Color, classicLight: Color, oceanDark: Color, oceanLight: Color): this(function = { isLight, isClassic -> if (isLight) if (isClassic) classicLight else oceanLight else if (isClassic) classicDark else oceanDark })
}
fun sessionColors(
isLight: Boolean,
isClassic: Boolean,
primary: Color = if (isClassic) primaryGreen else primaryBlue
): SessionColors {
val index = (if (isLight) 1 else 0) + if (isClassic) 0 else 2
return SessionColors(
isLight = isLight,
primary = primary,
danger = if (isLight) dangerLight else dangerDark,
disabled = if (isLight) disabledLioht else disabledDark,
background = listOf(Color.Black, Color.White, oceanDarkColors[2], oceanLightColors[7])[index],
backgroundSecondary = listOf(classicDarkColors[1], classicLightColors[5], oceanDarkColors[1], oceanLightColors[6])[index],
text = listOf(Color.White, Color.Black, Color.White, oceanLightColors[1])[index],
textSecondary = listOf(classicDarkColors[5], classicLightColors[1], oceanDarkColors[5], oceanLightColors[2])[index],
borders = listOf(classicDarkColors[3], classicLightColors[3], oceanDarkColors[4], oceanLightColors[3])[index],
textBubbleSent = listOf(Color.Black, Color.Black, Color.Black, oceanLightColors[1])[index],
backgroundBubbleReceived = listOf(classicDarkColors[2], classicLightColors[4], oceanDarkColors[4], oceanLightColors[4])[index],
textBubbleReceived = listOf(Color.White, classicLightColors[4], oceanDarkColors[4], oceanLightColors[4])[index],
)
}
@Composable
fun transparentButtonColors() = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent)

View File

@ -0,0 +1,21 @@
package org.thoughtcrime.securesms.ui
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
val LocalDimensions = staticCompositionLocalOf { Dimensions() }
data class Dimensions(
val itemSpacingTiny: Dp = 4.dp,
val itemSpacingExtraSmall: Dp = 8.dp,
val itemSpacingSmall: Dp = 16.dp,
val itemSpacingMedium: Dp = 24.dp,
val marginTiny: Dp = 8.dp,
val marginExtraExtraSmall: Dp = 12.dp,
val marginExtraSmall: Dp = 16.dp,
val marginSmall: Dp = 24.dp,
val marginMedium: Dp = 32.dp,
val marginLarge: Dp = 64.dp,
val dividerIndent: Dp = 80.dp,
)

View File

@ -0,0 +1,42 @@
package org.thoughtcrime.securesms.ui
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.sp
fun boldStyle(size: TextUnit) = TextStyle.Default.copy(
fontWeight = FontWeight.Bold,
fontSize = size
)
fun defaultStyle(size: TextUnit) = TextStyle.Default.copy(
fontSize = size,
lineHeight = size * 1.2
)
val sessionTypography = Typography(
h1 = boldStyle(36.sp),
h2 = boldStyle(32.sp),
h3 = boldStyle(29.sp),
h4 = boldStyle(26.sp),
h5 = boldStyle(23.sp),
h6 = boldStyle(20.sp),
)
val Typography.xl get() = defaultStyle(18.sp)
val Typography.large get() = defaultStyle(16.sp)
val Typography.base get() = defaultStyle(14.sp)
val Typography.baseBold get() = boldStyle(14.sp)
val Typography.baseMonospace get() = defaultStyle(14.sp).copy(fontFamily = FontFamily.Monospace)
val Typography.small get() = defaultStyle(12.sp)
val Typography.smallMonospace get() = defaultStyle(12.sp).copy(fontFamily = FontFamily.Monospace)
val Typography.extraSmall get() = defaultStyle(11.sp)
val Typography.extraSmallMonospace get() = defaultStyle(11.sp).copy(fontFamily = FontFamily.Monospace)
val Typography.fine get() = defaultStyle(9.sp)
val Typography.h7 get() = boldStyle(18.sp)
val Typography.h8 get() = boldStyle(16.sp)
val Typography.h9 get() = boldStyle(14.sp)

View File

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.ui
import android.content.Context
import androidx.annotation.AttrRes
import androidx.appcompat.view.ContextThemeWrapper
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.RoundedCornerShape
@ -11,132 +10,20 @@ import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.Colors
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButtonDefaults
import androidx.compose.material.Shapes
import androidx.compose.material.TabRowDefaults
import androidx.compose.material.Typography
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.google.accompanist.themeadapter.appcompat.createAppCompatTheme
import com.google.android.material.color.MaterialColors
import network.loki.messenger.R
import org.session.libsession.utilities.AppTextSecurePreferences
import org.thoughtcrime.securesms.util.ThemeState
import org.thoughtcrime.securesms.util.themeState
val LocalOnLightCell = staticCompositionLocalOf { Color.Black }
val LocalDimensions = staticCompositionLocalOf { Dimensions() }
data class Dimensions(
val itemSpacingTiny: Dp = 4.dp,
val itemSpacingExtraSmall: Dp = 8.dp,
val itemSpacingSmall: Dp = 16.dp,
val itemSpacingMedium: Dp = 24.dp,
val marginTiny: Dp = 8.dp,
val marginExtraExtraSmall: Dp = 12.dp,
val marginExtraSmall: Dp = 16.dp,
val marginSmall: Dp = 24.dp,
val marginMedium: Dp = 32.dp,
val marginLarge: Dp = 64.dp,
val dividerIndent: Dp = 80.dp,
)
val LocalColors = staticCompositionLocalOf { sessionColors(isLight = false, isClassic = true) }
data class SessionColors(
val isLight: Boolean = false,
val primary: Color = Color.Unspecified,
val danger: Color = Color.Unspecified,
val disabled: Color = Color.Unspecified,
val background: Color = Color.Unspecified,
val backgroundSecondary: Color = Color.Unspecified,
val text: Color = Color.Unspecified,
val textSecondary: Color = Color.Unspecified,
val borders: Color = Color.Unspecified,
val textBubbleSent: Color = Color.Unspecified,
val backgroundBubbleReceived: Color = Color.Unspecified,
val textBubbleReceived: Color = Color.Unspecified,
) {
val backgroundLight get() = if (isLight) backgroundSecondary else Color.White
val onBackgroundLight get() = if (isLight) text else background
val button get() = if (isLight) text else primary
val divider get() = text.copy(alpha = TabRowDefaults.DividerOpacity)
val backgroundBubbleSent get() = primary
@Composable fun radioButtonColors() = RadioButtonDefaults.colors(selectedColor = primary, unselectedColor = text, disabledColor = disabled)
}
val primaryGreen = Color(0xFF31F196)
val primaryBlue = Color(0xFF57C9FA)
val primaryPurple = Color(0xFFC993FF)
val primaryPink = Color(0xFFFF95EF)
val primaryRed = Color(0xFFFF9C8E)
val primaryOrange = Color(0xFFFCB159)
val primaryYellow = Color(0xFFFAD657)
val dangerDark = Color(0xFFFF3A3A)
val dangerLight = Color(0xFFE12D19)
val disabledDark = Color(0xFFA1A2A1)
val disabledLioht = Color(0xFF6D6D6D)
val primaryColors = listOf(
primaryGreen,
primaryBlue,
primaryPurple,
primaryPink,
primaryRed,
primaryOrange,
primaryYellow,
)
private class UnresolvedColor(val function: (Boolean, Boolean) -> Color) {
operator fun invoke(isLight: Boolean, isClassic: Boolean) = function(isLight, isClassic)
constructor(light: Color, dark: Color): this(function = { isLight, _ -> if (isLight) light else dark })
constructor(classicDark: Color, classicLight: Color, oceanDark: Color, oceanLight: Color): this(function = { isLight, isClassic -> if (isLight) if (isClassic) classicLight else oceanLight else if (isClassic) classicDark else oceanDark })
}
private fun sessionColors(
isLight: Boolean,
isClassic: Boolean,
primary: Color = if (isClassic) primaryGreen else primaryBlue
): SessionColors {
val index = (if (isLight) 1 else 0) + if (isClassic) 0 else 2
return SessionColors(
isLight = isLight,
primary = primary,
danger = if (isLight) dangerLight else dangerDark,
disabled = if (isLight) disabledLioht else disabledDark,
background = listOf(Color.Black, Color.White, oceanDarkColors[2], oceanLightColors[7])[index],
backgroundSecondary = listOf(classicDarkColors[1], classicLightColors[5], oceanDarkColors[1], oceanLightColors[6])[index],
text = listOf(Color.White, Color.Black, Color.White, oceanLightColors[1])[index],
textSecondary = listOf(classicDarkColors[5], classicLightColors[1], oceanDarkColors[5], oceanLightColors[2])[index],
borders = listOf(classicDarkColors[3], classicLightColors[3], oceanDarkColors[4], oceanLightColors[3])[index],
textBubbleSent = listOf(Color.Black, Color.Black, Color.Black, oceanLightColors[1])[index],
backgroundBubbleReceived = listOf(classicDarkColors[2], classicLightColors[4], oceanDarkColors[4], oceanLightColors[4])[index],
textBubbleReceived = listOf(Color.White, classicLightColors[4], oceanDarkColors[4], oceanLightColors[4])[index],
)
}
private fun Context.sessionColors() = AppTextSecurePreferences(this).themeState().sessionColors()
private fun ThemeState.sessionColors() = sessionColors(isLight, isClassic, accent)
/**
* Sets a Material2 compose theme based on your selections in SharedPreferences.
* Apply a Material2 compose theme based on user selections in SharedPreferences.
*/
@Composable
fun SessionMaterialTheme(
@ -145,9 +32,8 @@ fun SessionMaterialTheme(
SessionMaterialTheme(LocalContext.current.sessionColors()) { content() }
}
/**
*
* Apply a given [SessionColors], and our typography and shapes as a Material 2 Compose Theme.
**/
@Composable
fun SessionMaterialTheme(
@ -190,49 +76,15 @@ private fun SessionColors.toMaterialColors() = Colors(
isLight = isLight
)
private fun Context.sessionColors() = AppTextSecurePreferences(this).themeState().sessionColors()
private fun ThemeState.sessionColors() = sessionColors(isLight, isClassic, accent)
val sessionShapes = Shapes(
small = RoundedCornerShape(50)
)
fun boldStyle(size: TextUnit) = TextStyle.Default.copy(
fontWeight = FontWeight.Bold,
fontSize = size
)
fun defaultStyle(size: TextUnit) = TextStyle.Default.copy(
fontSize = size,
lineHeight = size * 1.2
)
val sessionTypography = Typography(
h1 = boldStyle(36.sp),
h2 = boldStyle(32.sp),
h3 = boldStyle(29.sp),
h4 = boldStyle(26.sp),
h5 = boldStyle(23.sp),
h6 = boldStyle(20.sp),
)
val Typography.xl get() = defaultStyle(18.sp)
val Typography.large get() = defaultStyle(16.sp)
val Typography.base get() = defaultStyle(14.sp)
val Typography.baseBold get() = boldStyle(14.sp)
val Typography.baseMonospace get() = defaultStyle(14.sp).copy(fontFamily = FontFamily.Monospace)
val Typography.small get() = defaultStyle(12.sp)
val Typography.smallMonospace get() = defaultStyle(12.sp).copy(fontFamily = FontFamily.Monospace)
val Typography.extraSmall get() = defaultStyle(11.sp)
val Typography.extraSmallMonospace get() = defaultStyle(11.sp).copy(fontFamily = FontFamily.Monospace)
val Typography.fine get() = defaultStyle(9.sp)
val Typography.h7 get() = boldStyle(18.sp)
val Typography.h8 get() = boldStyle(16.sp)
val Typography.h9 get() = boldStyle(14.sp)
fun Context.getColorFromTheme(@AttrRes attr: Int, defaultValue: Int = 0x0): Color =
MaterialColors.getColor(this, attr, defaultValue).let(::Color)
/**
* Set the theme and a background for Compose Previews.
* Set the Material 2 theme and a background for Compose Previews.
*/
@Composable
fun PreviewTheme(

View File

@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@ -26,7 +25,6 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
@ -34,7 +32,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.LocalColors
import org.thoughtcrime.securesms.ui.LocalOnLightCell
import org.thoughtcrime.securesms.util.QRCodeUtilities
@Composable
@ -47,13 +44,11 @@ fun QrImage(
mutableStateOf(null)
}
val dark = LocalColors.current.onBackgroundLight.toArgb()
val scope = rememberCoroutineScope()
LaunchedEffect(string) {
scope.launch(Dispatchers.IO) {
bitmap = (300..500 step 100).firstNotNullOf {
runCatching { QRCodeUtilities.encode(string, it, dark = dark) }.getOrNull()
runCatching { QRCodeUtilities.encode(string, it) }.getOrNull()
}
}
}
@ -74,7 +69,7 @@ private fun Content(
bitmap: Bitmap?,
icon: Int,
modifier: Modifier = Modifier,
qrColor: Color = LocalOnLightCell.current,
qrColor: Color = LocalColors.current.onBackgroundLight,
backgroundColor: Color,
) {
Box(
@ -104,7 +99,7 @@ private fun Content(
Icon(
painter = painterResource(id = icon),
contentDescription = "",
tint = LocalOnLightCell.current,
tint = qrColor,
modifier = Modifier
.size(62.dp)
.align(Alignment.Center)