Organise and fix a few button colors

This commit is contained in:
Andrew 2024-06-15 00:36:46 +09:30
parent e44b401bd5
commit f3d90e3adb
6 changed files with 207 additions and 180 deletions

View File

@ -118,7 +118,6 @@ private fun InviteFriend(
SlimOutlineCopyButton(
modifier = Modifier.weight(1f),
color = LocalColors.current.text,
onClick = copyPublicKey
)
}

View File

@ -90,6 +90,8 @@ interface Colors {
val qrCodeBackground: Color
}
val Colors.slimOutlineButton: Color get() = text
fun sessionColors(
isLight: Boolean,
isClassic: Boolean,

View File

@ -40,7 +40,6 @@ sealed class GetString {
data class FromMap<T>(val value: T, val function: (Context, T) -> String): GetString() {
@Composable
override fun string(): String = function(LocalContext.current, value)
override fun string(context: Context): String = function(context, value)
}
}

View File

@ -2,28 +2,46 @@ package org.thoughtcrime.securesms.ui.components
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.PressInteraction
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ButtonColors
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.ButtonElevation
import androidx.compose.material.MaterialTheme
import androidx.compose.material.RadioButton
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import network.loki.messenger.R
import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.LaunchedEffectAsync
import org.thoughtcrime.securesms.ui.LocalColors
import org.thoughtcrime.securesms.ui.baseBold
import org.thoughtcrime.securesms.ui.contentDescription
@ -31,7 +49,185 @@ import org.thoughtcrime.securesms.ui.extraSmall
import org.thoughtcrime.securesms.ui.h8
import org.thoughtcrime.securesms.ui.h9
import org.thoughtcrime.securesms.ui.radioButtonColors
import org.thoughtcrime.securesms.ui.slimOutlineButton
import org.thoughtcrime.securesms.ui.small
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
interface ButtonType {
@Composable fun border(color: Color, enabled: Boolean): BorderStroke?
@Composable fun buttonColors(color: Color): ButtonColors
val elevation: ButtonElevation? @Composable get
object Outline: ButtonType {
@Composable override fun border(color: Color, enabled: Boolean) = BorderStroke(1.dp, if (enabled) color else LocalColors.current.disabled)
@Composable override fun buttonColors(color: Color) = ButtonDefaults.buttonColors(
contentColor = color,
backgroundColor = Color.Unspecified,
disabledContentColor = LocalColors.current.disabled,
disabledBackgroundColor = Color.Unspecified
)
override val elevation: ButtonElevation? @Composable get() = null
}
object Fill: ButtonType {
@Composable override fun border(color: Color, enabled: Boolean) = null
@Composable override fun buttonColors(color: Color) = ButtonDefaults.buttonColors(
contentColor = LocalColors.current.background,
backgroundColor = color,
disabledContentColor = LocalColors.current.disabled,
disabledBackgroundColor = Color.Unspecified
)
override val elevation: ButtonElevation @Composable get() = ButtonDefaults.elevation()
}
}
/**
* Base [Button] implementation
*/
@Composable
fun Button(
onClick: () -> Unit,
color: Color,
type: ButtonType,
modifier: Modifier = Modifier,
enabled: Boolean = true,
size: ButtonSize = ButtonSize.Large,
elevation: ButtonElevation? = type.elevation,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = type.border(color, enabled),
colors: ButtonColors = type.buttonColors(color),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable RowScope.() -> Unit
) {
size.applyConstraints {
androidx.compose.material.Button(
onClick,
modifier.heightIn(min = size.minHeight),
enabled,
interactionSource,
elevation,
shape,
border,
colors,
content = content
)
}
}
/**
* Courtesy [Button] implementation for buttons that just display text.
*/
@Composable
fun Button(
text: String,
onClick: () -> Unit,
color: Color,
type: ButtonType,
modifier: Modifier = Modifier,
enabled: Boolean = true,
size: ButtonSize = ButtonSize.Large,
elevation: ButtonElevation? = type.elevation,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = type.border(color, enabled),
colors: ButtonColors = type.buttonColors(color),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
Button(onClick, color, type, modifier, enabled, size, elevation, shape, border, colors, interactionSource) {
Text(text)
}
}
@Composable fun FillButton(text: String, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, LocalColors.current.buttonOutline, ButtonType.Fill, modifier, enabled)
}
@Composable fun PrimaryFillButton(text: String, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, LocalColors.current.primary, ButtonType.Fill, modifier, enabled)
}
@Composable fun OutlineButton(text: String, modifier: Modifier = Modifier, color: Color = LocalColors.current.buttonOutline, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, color, ButtonType.Outline, modifier, enabled)
}
@Composable fun PrimaryOutlineButton(text: String, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, LocalColors.current.primary, ButtonType.Outline, modifier, enabled)
}
@Composable fun SlimOutlineButton(onClick: () -> Unit, modifier: Modifier = Modifier, color: Color = LocalColors.current.slimOutlineButton, enabled: Boolean = true, content: @Composable () -> Unit) {
Button(onClick, color, ButtonType.Outline, modifier, enabled, ButtonSize.Slim) { content() }
}
/**
* Courtesy [SlimOutlineButton] implementation for buttons that just display text.
*/
@Composable fun SlimOutlineButton(text: String, modifier: Modifier = Modifier, color: Color = LocalColors.current.slimOutlineButton, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, color, ButtonType.Outline, modifier, enabled, ButtonSize.Slim)
}
@Composable
fun SlimOutlineCopyButton(
modifier: Modifier = Modifier,
color: Color = LocalColors.current.slimOutlineButton,
onClick: () -> Unit
) {
OutlineCopyButton(modifier, ButtonSize.Slim, color, onClick)
}
@Composable
fun OutlineCopyButton(
modifier: Modifier = Modifier,
size: ButtonSize = ButtonSize.Large,
color: Color = LocalColors.current.buttonOutline,
onClick: () -> Unit
) {
val interactionSource = remember { MutableInteractionSource() }
Button(
modifier = modifier.contentDescription(R.string.AccessibilityId_copy_button),
interactionSource = interactionSource,
size = size,
type = ButtonType.Outline,
color = color,
onClick = onClick
) {
TemporaryClickedContent(
interactionSource = interactionSource,
content = { Text(stringResource(R.string.copy)) },
temporaryContent = { Text(stringResource(R.string.copied)) }
)
}
}
@Composable
fun TemporaryClickedContent(
interactionSource: MutableInteractionSource,
content: @Composable () -> Unit,
temporaryContent: @Composable () -> Unit,
temporaryDelay: Duration = 2.seconds
) {
var clicked by remember { mutableStateOf(false) }
LaunchedEffectAsync {
interactionSource.releases.collectLatest {
clicked = true
delay(temporaryDelay)
clicked = false
}
}
// Using a Box because the Buttons add children in a Row
// and they will jank as they are added and removed.
Box(contentAlignment = Alignment.Center) {
AnimatedVisibility(!clicked, enter = fadeIn(), exit = fadeOut()) {
content()
}
AnimatedVisibility(clicked, enter = fadeIn(), exit = fadeOut()) {
temporaryContent()
}
}
}
@Composable
fun BorderlessButton(

View File

@ -6,7 +6,6 @@ import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement
import androidx.compose.material.LocalTextStyle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@ -27,14 +26,15 @@ interface ButtonSize {
val textStyle: TextStyle @Composable get
val minHeight: Dp
}
object LargeButtonSize: ButtonSize {
override val textStyle @Composable get() = baseBold
override val minHeight = 41.dp
}
object Large: ButtonSize {
override val textStyle @Composable get() = baseBold
override val minHeight = 41.dp
}
object Slim: ButtonSize {
override val textStyle @Composable get() = extraSmallBold
override val minHeight = 29.dp
}
object SlimButtonSize: ButtonSize {
override val textStyle @Composable get() = extraSmallBold
override val minHeight = 29.dp
}

View File

@ -33,172 +33,3 @@ import org.thoughtcrime.securesms.ui.contentDescription
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
interface ButtonType {
@Composable fun border(color: Color, enabled: Boolean): BorderStroke?
@Composable fun buttonColors(color: Color): ButtonColors
val elevation: ButtonElevation? @Composable get
object Outline: ButtonType {
@Composable override fun border(color: Color, enabled: Boolean) = BorderStroke(1.dp, if (enabled) color else LocalColors.current.disabled)
@Composable override fun buttonColors(color: Color) = ButtonDefaults.buttonColors(
contentColor = color,
backgroundColor = Color.Unspecified,
disabledContentColor = LocalColors.current.disabled,
disabledBackgroundColor = Color.Unspecified
)
override val elevation: ButtonElevation? @Composable get() = null
}
object Fill: ButtonType {
@Composable override fun border(color: Color, enabled: Boolean) = null
@Composable override fun buttonColors(color: Color) = ButtonDefaults.buttonColors(
contentColor = LocalColors.current.background,
backgroundColor = color,
disabledContentColor = LocalColors.current.disabled,
disabledBackgroundColor = Color.Unspecified
)
override val elevation: ButtonElevation? @Composable get() = ButtonDefaults.elevation()
}
}
/**
* Base [Button] implementation
*/
@Composable
fun Button(
onClick: () -> Unit,
color: Color,
type: ButtonType,
modifier: Modifier = Modifier,
enabled: Boolean = true,
size: ButtonSize = LargeButtonSize,
elevation: ButtonElevation? = type.elevation,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = type.border(color, enabled),
colors: ButtonColors = type.buttonColors(color),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
content: @Composable RowScope.() -> Unit
) {
size.applyConstraints {
androidx.compose.material.Button(
onClick,
modifier.heightIn(min = size.minHeight),
enabled,
interactionSource,
elevation,
shape,
border,
colors,
content = content
)
}
}
/**
* Courtesy [Button] implementation
*/
@Composable
fun Button(
text: String,
onClick: () -> Unit,
color: Color,
type: ButtonType,
modifier: Modifier = Modifier,
enabled: Boolean = true,
size: ButtonSize = LargeButtonSize,
elevation: ButtonElevation? = type.elevation,
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = type.border(color, enabled),
colors: ButtonColors = type.buttonColors(color),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
Button(onClick, color, type, modifier, enabled, size, elevation, shape, border, colors, interactionSource) {
Text(text)
}
}
@Composable fun FillButton(text: String, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, LocalColors.current.buttonOutline, ButtonType.Fill, modifier, enabled)
}
@Composable fun PrimaryFillButton(text: String, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, LocalColors.current.primary, ButtonType.Fill, modifier, enabled)
}
@Composable fun OutlineButton(text: String, modifier: Modifier = Modifier, color: Color = LocalColors.current.buttonOutline, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, color, ButtonType.Outline, modifier, enabled)
}
@Composable fun PrimaryOutlineButton(text: String, modifier: Modifier = Modifier, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, LocalColors.current.primary, ButtonType.Outline, modifier, enabled)
}
@Composable fun SlimOutlineButton(text: String, modifier: Modifier = Modifier, color: Color = LocalColors.current.buttonOutline, enabled: Boolean = true, onClick: () -> Unit) {
Button(text, onClick, color, ButtonType.Outline, modifier, enabled, SlimButtonSize)
}
@Composable fun SlimOutlineButton(onClick: () -> Unit, modifier: Modifier = Modifier, color: Color = LocalColors.current.buttonOutline, enabled: Boolean = true, content: @Composable () -> Unit) {
Button(onClick, color, ButtonType.Outline, modifier, enabled, SlimButtonSize) { content() }
}
@Composable
fun SlimOutlineCopyButton(
modifier: Modifier = Modifier,
color: Color = LocalColors.current.buttonOutline,
onClick: () -> Unit
) {
OutlineCopyButton(modifier, SlimButtonSize, color, onClick)
}
@Composable
fun OutlineCopyButton(
modifier: Modifier = Modifier,
size: ButtonSize = LargeButtonSize,
color: Color = LocalColors.current.buttonOutline,
onClick: () -> Unit
) {
val interactionSource = remember { MutableInteractionSource() }
Button(
modifier = modifier.contentDescription(R.string.AccessibilityId_copy_button),
interactionSource = interactionSource,
size = size,
type = ButtonType.Outline,
color = color,
onClick = onClick
) {
TemporaryClickedContent(
interactionSource = interactionSource,
content = { Text(stringResource(R.string.copy)) },
temporaryContent = { Text(stringResource(R.string.copied)) }
)
}
}
@Composable
fun TemporaryClickedContent(
interactionSource: MutableInteractionSource,
content: @Composable () -> Unit,
temporaryContent: @Composable () -> Unit,
temporaryDelay: Duration = 2.seconds
) {
var clicked by remember { mutableStateOf(false) }
LaunchedEffectAsync {
interactionSource.releases.collectLatest {
clicked = true
delay(temporaryDelay)
clicked = false
}
}
// Using a Box because the Buttons add children in a Row
// and they will jank as they are added and removed.
Box(contentAlignment = Alignment.Center) {
AnimatedVisibility(!clicked, enter = fadeIn(), exit = fadeOut()) {
content()
}
AnimatedVisibility(clicked, enter = fadeIn(), exit = fadeOut()) {
temporaryContent()
}
}
}