secant-android-wallet/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt

360 lines
12 KiB
Kotlin
Raw Normal View History

package co.electriccoin.zcash.ui.design.component
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
2022-01-13 09:49:08 -08:00
import androidx.compose.foundation.layout.Column
[#808] Use Dimens Across App * [#808] Use Dimens across the app - Shorter Onboarding screen design enhanced * Shorter New Wallet Backup screen UI enhanced * Home screen UI enhance * Seed screen UI enhance * Link SendArgumentsWrapper issue * Move custom buttons paddings to PaddingValues - Optional Modifier parameter should have a default value of Modifier - Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings - Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs. * Settings screen enhance * Support screen UI enhance * About screen UI enhance * Scan screen UI enhance - Move modifiers to the caller’s side - Match texts to predefined styles - Add color to Small text component + reorder parameters * Send screen UI enahnce - Lifted out some modifiers arguments to the caller’s side - Fixed button’s horizontal paddings * Scan move BackHandler to Android class * Update screen UI enhance * Receive screen UI enhance * Address screen UI enhance * Ignore AndroidUpdate back action test - Will be refactored soon * Remove TODO as already implemented * Restore screens UI enhance * Create UpdateViewAndroidTest * [#807][Design system] Remove deprecated Paddings * [#705] Instrumentation coverage generation fails locally * [#808] Use Dimens across the app - Shorter Onboarding screen design enhanced * Shorter New Wallet Backup screen UI enhanced * Home screen UI enhance * Seed screen UI enhance * Link SendArgumentsWrapper issue * Move custom buttons paddings to PaddingValues - Optional Modifier parameter should have a default value of Modifier - Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings - Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs. * Settings screen enhance * Support screen UI enhance * About screen UI enhance * Scan screen UI enhance - Move modifiers to the caller’s side - Match texts to predefined styles - Add color to Small text component + reorder parameters * Send screen UI enahnce - Lifted out some modifiers arguments to the caller’s side - Fixed button’s horizontal paddings * Scan move BackHandler to Android class * Update screen UI enhance * Receive screen UI enhance * Address screen UI enhance * Ignore AndroidUpdate back action test - Will be refactored soon * Remove TODO as already implemented * Restore screens UI enhance * Create UpdateViewAndroidTest * [#807][Design system] Remove deprecated Paddings * Address review comments
2023-04-04 05:21:18 -07:00
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
2022-02-21 06:33:40 -08:00
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ButtonDefaults.buttonColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.PaintingStyle
2023-08-04 04:01:16 -07:00
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.style.TextAlign
2022-01-13 09:49:08 -08:00
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
2022-01-13 09:49:08 -08:00
@Preview
@Composable
private fun ButtonComposablePreview() {
ZcashTheme(forceDarkMode = false) {
2022-01-13 09:49:08 -08:00
GradientSurface {
Column {
PrimaryButton(onClick = { }, text = "Primary")
SecondaryButton(onClick = { }, text = "Secondary")
TertiaryButton(onClick = { }, text = "Tertiary")
NavigationButton(onClick = { }, text = "Navigation")
DangerousButton(onClick = { }, text = "Dangerous")
2022-01-13 09:49:08 -08:00
}
}
}
}
@Composable
@Suppress("LongParameterList")
fun PrimaryButton(
onClick: () -> Unit,
text: String,
2022-02-17 05:08:06 -08:00
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues =
PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true,
buttonColor: Color = MaterialTheme.colorScheme.primary,
textColor: Color = MaterialTheme.colorScheme.onPrimary,
) {
Button(
2023-08-04 04:01:16 -07:00
shape = RectangleShape,
2022-02-17 05:08:06 -08:00
enabled = enabled,
modifier =
modifier.then(Modifier.fillMaxWidth())
.padding(outerPaddingValues)
.shadow(
contentColor = textColor,
strokeColor = buttonColor,
strokeWidth = 1.dp,
offsetX = ZcashTheme.dimens.buttonShadowOffsetX,
offsetY = ZcashTheme.dimens.buttonShadowOffsetY,
spread = ZcashTheme.dimens.buttonShadowSpread,
)
.translationClick(
// + 6dp to exactly cover the bottom shadow
translationX = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp,
translationY = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp
)
.defaultMinSize(ZcashTheme.dimens.buttonWidth, ZcashTheme.dimens.buttonHeight)
.border(1.dp, Color.Black),
colors =
buttonColors(
containerColor = buttonColor,
disabledContainerColor = ZcashTheme.colors.disabledButtonColor,
disabledContentColor = ZcashTheme.colors.disabledButtonTextColor
),
onClick = onClick,
) {
Text(
style = ZcashTheme.extendedTypography.buttonText,
textAlign = TextAlign.Center,
text = text.uppercase(),
color = textColor
)
}
}
@Composable
@Suppress("LongParameterList")
fun SecondaryButton(
onClick: () -> Unit,
text: String,
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues =
PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true,
buttonColor: Color = MaterialTheme.colorScheme.secondary,
textColor: Color = MaterialTheme.colorScheme.onSecondary,
) {
Button(
2023-08-04 04:01:16 -07:00
shape = RectangleShape,
enabled = enabled,
modifier =
modifier.then(Modifier.fillMaxWidth())
.padding(outerPaddingValues)
.shadow(
contentColor = textColor,
strokeColor = textColor,
offsetX = ZcashTheme.dimens.buttonShadowOffsetX,
offsetY = ZcashTheme.dimens.buttonShadowOffsetY,
spread = ZcashTheme.dimens.buttonShadowSpread,
)
.translationClick(
// + 6dp to exactly cover the bottom shadow
translationX = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp,
translationY = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp
)
.defaultMinSize(ZcashTheme.dimens.buttonWidth, ZcashTheme.dimens.buttonHeight)
.border(1.dp, Color.Black),
colors =
buttonColors(
containerColor = buttonColor,
disabledContainerColor = ZcashTheme.colors.disabledButtonColor,
disabledContentColor = ZcashTheme.colors.disabledButtonTextColor
),
onClick = onClick,
) {
2022-02-21 06:33:40 -08:00
Text(
style = ZcashTheme.extendedTypography.buttonText,
textAlign = TextAlign.Center,
text = text.uppercase(),
color = textColor
2022-02-21 06:33:40 -08:00
)
}
}
@Composable
fun NavigationButton(
onClick: () -> Unit,
text: String,
[#808] Use Dimens Across App * [#808] Use Dimens across the app - Shorter Onboarding screen design enhanced * Shorter New Wallet Backup screen UI enhanced * Home screen UI enhance * Seed screen UI enhance * Link SendArgumentsWrapper issue * Move custom buttons paddings to PaddingValues - Optional Modifier parameter should have a default value of Modifier - Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings - Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs. * Settings screen enhance * Support screen UI enhance * About screen UI enhance * Scan screen UI enhance - Move modifiers to the caller’s side - Match texts to predefined styles - Add color to Small text component + reorder parameters * Send screen UI enahnce - Lifted out some modifiers arguments to the caller’s side - Fixed button’s horizontal paddings * Scan move BackHandler to Android class * Update screen UI enhance * Receive screen UI enhance * Address screen UI enhance * Ignore AndroidUpdate back action test - Will be refactored soon * Remove TODO as already implemented * Restore screens UI enhance * Create UpdateViewAndroidTest * [#807][Design system] Remove deprecated Paddings * [#705] Instrumentation coverage generation fails locally * [#808] Use Dimens across the app - Shorter Onboarding screen design enhanced * Shorter New Wallet Backup screen UI enhanced * Home screen UI enhance * Seed screen UI enhance * Link SendArgumentsWrapper issue * Move custom buttons paddings to PaddingValues - Optional Modifier parameter should have a default value of Modifier - Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings - Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs. * Settings screen enhance * Support screen UI enhance * About screen UI enhance * Scan screen UI enhance - Move modifiers to the caller’s side - Match texts to predefined styles - Add color to Small text component + reorder parameters * Send screen UI enahnce - Lifted out some modifiers arguments to the caller’s side - Fixed button’s horizontal paddings * Scan move BackHandler to Android class * Update screen UI enhance * Receive screen UI enhance * Address screen UI enhance * Ignore AndroidUpdate back action test - Will be refactored soon * Remove TODO as already implemented * Restore screens UI enhance * Create UpdateViewAndroidTest * [#807][Design system] Remove deprecated Paddings * Address review comments
2023-04-04 05:21:18 -07:00
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues =
PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
) {
Button(
2023-08-04 04:01:16 -07:00
shape = RectangleShape,
onClick = onClick,
modifier =
modifier.then(
Modifier
.padding(outerPaddingValues)
),
2022-02-21 06:33:40 -08:00
colors = buttonColors(containerColor = MaterialTheme.colorScheme.secondary)
) {
Text(
style = MaterialTheme.typography.labelLarge,
textAlign = TextAlign.Center,
text = text,
color = MaterialTheme.colorScheme.onSecondary
)
}
}
@Composable
fun TertiaryButton(
onClick: () -> Unit,
text: String,
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues =
PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true
) {
Button(
2023-08-04 04:01:16 -07:00
shape = RectangleShape,
onClick = onClick,
modifier =
modifier.then(
Modifier
.fillMaxWidth()
.padding(outerPaddingValues)
),
enabled = enabled,
2022-02-21 06:33:40 -08:00
elevation = ButtonDefaults.buttonElevation(0.dp, 0.dp, 0.dp),
colors = buttonColors(containerColor = ZcashTheme.colors.tertiary)
) {
2022-02-21 06:33:40 -08:00
Text(
style = MaterialTheme.typography.labelLarge,
textAlign = TextAlign.Center,
2022-02-21 06:33:40 -08:00
text = text,
color = ZcashTheme.colors.onTertiary
)
}
}
@Composable
fun DangerousButton(
onClick: () -> Unit,
text: String,
[#808] Use Dimens Across App * [#808] Use Dimens across the app - Shorter Onboarding screen design enhanced * Shorter New Wallet Backup screen UI enhanced * Home screen UI enhance * Seed screen UI enhance * Link SendArgumentsWrapper issue * Move custom buttons paddings to PaddingValues - Optional Modifier parameter should have a default value of Modifier - Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings - Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs. * Settings screen enhance * Support screen UI enhance * About screen UI enhance * Scan screen UI enhance - Move modifiers to the caller’s side - Match texts to predefined styles - Add color to Small text component + reorder parameters * Send screen UI enahnce - Lifted out some modifiers arguments to the caller’s side - Fixed button’s horizontal paddings * Scan move BackHandler to Android class * Update screen UI enhance * Receive screen UI enhance * Address screen UI enhance * Ignore AndroidUpdate back action test - Will be refactored soon * Remove TODO as already implemented * Restore screens UI enhance * Create UpdateViewAndroidTest * [#807][Design system] Remove deprecated Paddings * [#705] Instrumentation coverage generation fails locally * [#808] Use Dimens across the app - Shorter Onboarding screen design enhanced * Shorter New Wallet Backup screen UI enhanced * Home screen UI enhance * Seed screen UI enhance * Link SendArgumentsWrapper issue * Move custom buttons paddings to PaddingValues - Optional Modifier parameter should have a default value of Modifier - Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings - Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs. * Settings screen enhance * Support screen UI enhance * About screen UI enhance * Scan screen UI enhance - Move modifiers to the caller’s side - Match texts to predefined styles - Add color to Small text component + reorder parameters * Send screen UI enahnce - Lifted out some modifiers arguments to the caller’s side - Fixed button’s horizontal paddings * Scan move BackHandler to Android class * Update screen UI enhance * Receive screen UI enhance * Address screen UI enhance * Ignore AndroidUpdate back action test - Will be refactored soon * Remove TODO as already implemented * Restore screens UI enhance * Create UpdateViewAndroidTest * [#807][Design system] Remove deprecated Paddings * Address review comments
2023-04-04 05:21:18 -07:00
modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues =
PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
) {
Button(
2023-08-04 04:01:16 -07:00
shape = RectangleShape,
onClick = onClick,
modifier =
modifier.then(
Modifier
.fillMaxWidth()
.padding(outerPaddingValues)
),
2022-02-21 06:33:40 -08:00
colors = buttonColors(containerColor = ZcashTheme.colors.dangerous)
) {
2022-02-21 06:33:40 -08:00
Text(
style = MaterialTheme.typography.labelLarge,
textAlign = TextAlign.Center,
2022-02-21 06:33:40 -08:00
text = text,
color = ZcashTheme.colors.onDangerous
)
}
}
@Suppress("LongParameterList")
fun Modifier.shadow(
contentColor: Color = Color.Black,
strokeColor: Color = Color.Black,
strokeWidth: Dp = 2.dp,
offsetY: Dp = 0.dp,
offsetX: Dp = 0.dp,
spread: Dp = 0f.dp,
modifier: Modifier = Modifier
) = this.then(
modifier.drawBehind {
this.drawIntoCanvas {
val strokePaint = Paint()
strokePaint.style = PaintingStyle.Stroke
strokePaint.strokeWidth = strokeWidth.toPx()
val contentPaint = Paint()
strokePaint.style = PaintingStyle.Fill
// Reusable inner function to be able to reach modifier and canvas properties
fun drawShadowLayer(
paint: Paint,
color: Color,
paddingWidth: Float
) {
val frameworkPaint = paint.asFrameworkPaint()
val spreadPixel = spread.toPx()
val leftPixel = (0f - spreadPixel) + offsetX.toPx()
val topPixel = (0f - spreadPixel) + offsetY.toPx()
val rightPixel = (this.size.width + spreadPixel)
val bottomPixel = (this.size.height + spreadPixel)
frameworkPaint.color = color.toArgb()
it.drawRoundRect(
left = leftPixel + paddingWidth,
top = topPixel + paddingWidth,
right = rightPixel - paddingWidth,
bottom = bottomPixel - paddingWidth,
radiusX = 0f,
radiusY = 0f,
paint
)
}
// Draw stroke and then content paints
drawShadowLayer(strokePaint, strokeColor, 0f)
drawShadowLayer(contentPaint, contentColor, strokeWidth.toPx())
}
}
)
private enum class ButtonState { Pressed, Idle }
fun Modifier.translationClick(
translationX: Dp = 0.dp,
translationY: Dp = 0.dp
) = composed {
var buttonState by remember { mutableStateOf(ButtonState.Idle) }
val translationXAnimated by animateFloatAsState(
targetValue =
if (buttonState == ButtonState.Pressed) {
translationX.value
} else {
0f
},
label = "ClickTranslationXAnimation",
animationSpec =
tween(
durationMillis = 100
)
)
val translationYAnimated by animateFloatAsState(
targetValue =
if (buttonState == ButtonState.Pressed) {
translationY.value
} else {
0f
},
label = "ClickTranslationYAnimation",
animationSpec =
tween(
durationMillis = 100
)
)
this
.graphicsLayer {
this.translationX = translationXAnimated
this.translationY = translationYAnimated
}
.pointerInput(buttonState) {
awaitPointerEventScope {
buttonState =
if (buttonState == ButtonState.Pressed) {
waitForUpOrCancellation()
ButtonState.Idle
} else {
awaitFirstDown(false)
ButtonState.Pressed
}
}
}
}