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.ContentAlpha
import androidx.compose.material.LocalContentColor import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButtonDefaults
import androidx.compose.material.TabRowDefaults
import androidx.compose.material.Text import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.primarySurface import androidx.compose.material.primarySurface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import org.session.libsession.utilities.AppTextSecurePreferences
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.util.ThemeState
import org.thoughtcrime.securesms.util.themeState
const val classicDark0 = 0xff111111 const val classicDark0 = 0xff111111
const val classicDark1 = 0xff1B1B1B const val classicDark1 = 0xff1B1B1B
@ -67,6 +73,82 @@ val classicDarkColors = classicDarks.map(::Color)
val blackAlpha40 = Color.Black.copy(alpha = 0.4f) 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 @Composable
fun transparentButtonColors() = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent) 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 android.content.Context
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.appcompat.view.ContextThemeWrapper
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.shape.RoundedCornerShape 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.Colors
import androidx.compose.material.LocalContentColor import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButtonDefaults
import androidx.compose.material.Shapes import androidx.compose.material.Shapes
import androidx.compose.material.TabRowDefaults
import androidx.compose.material.Typography
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext 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.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 com.google.android.material.color.MaterialColors
import network.loki.messenger.R
import org.session.libsession.utilities.AppTextSecurePreferences import org.session.libsession.utilities.AppTextSecurePreferences
import org.thoughtcrime.securesms.util.ThemeState import org.thoughtcrime.securesms.util.ThemeState
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 @Composable
fun SessionMaterialTheme( fun SessionMaterialTheme(
@ -145,9 +32,8 @@ fun SessionMaterialTheme(
SessionMaterialTheme(LocalContext.current.sessionColors()) { content() } SessionMaterialTheme(LocalContext.current.sessionColors()) { content() }
} }
/** /**
* * Apply a given [SessionColors], and our typography and shapes as a Material 2 Compose Theme.
**/ **/
@Composable @Composable
fun SessionMaterialTheme( fun SessionMaterialTheme(
@ -190,49 +76,15 @@ private fun SessionColors.toMaterialColors() = Colors(
isLight = isLight isLight = isLight
) )
private fun Context.sessionColors() = AppTextSecurePreferences(this).themeState().sessionColors()
private fun ThemeState.sessionColors() = sessionColors(isLight, isClassic, accent)
val sessionShapes = Shapes( val sessionShapes = Shapes(
small = RoundedCornerShape(50) 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 @Composable
fun PreviewTheme( fun PreviewTheme(

View File

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