Skip to content
Open
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
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.compose.material.icons.core)
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.navigation.runtime.ktx)
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/com/example/cahier/core/ui/theme/Color.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

// Warning Colors - Light Mode
val LightWarning = Color(0xFFF57C00)
val LightOnWarning = Color(0xFFFFFFFF)
val LightWarningContainer = Color(0xFFFFF3E0)
val LightOnWarningContainer = Color(0xFFE65100)

// Warning Colors - Dark Mode
val DarkWarning = Color(0xFFFFB74D)
val DarkOnWarning = Color(0xFF4E342E)
val DarkWarningContainer = Color(0xFFE65100)
val DarkOnWarningContainer = Color(0xFFFFCC80)

// Brush Designer: color picker presets
val BrushBlack = Color(0xFF000000)
val BrushRed = Color(0xFFFF0000)
Expand Down
55 changes: 50 additions & 5 deletions app/src/main/java/com/example/cahier/core/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,51 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat

@Immutable
data class ExtendedColorScheme(
val warning: Color,
val onWarning: Color,
val warningContainer: Color,
val onWarningContainer: Color,
)

val LocalExtendedColorScheme = staticCompositionLocalOf {
ExtendedColorScheme(
warning = Color.Unspecified,
onWarning = Color.Unspecified,
warningContainer = Color.Unspecified,
onWarningContainer = Color.Unspecified,
)
}

val MaterialTheme.extendedColorScheme: ExtendedColorScheme
@Composable get() = LocalExtendedColorScheme.current

private val LightExtendedColorScheme =
ExtendedColorScheme(
warning = LightWarning,
onWarning = LightOnWarning,
warningContainer = LightWarningContainer,
onWarningContainer = LightOnWarningContainer,
)

private val DarkExtendedColorScheme =
ExtendedColorScheme(
warning = DarkWarning,
onWarning = DarkOnWarning,
warningContainer = DarkWarningContainer,
onWarningContainer = DarkOnWarningContainer,
)

private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
Expand Down Expand Up @@ -59,6 +99,9 @@ fun CahierAppTheme(
darkTheme -> DarkColorScheme
else -> LightColorScheme
}

val extendedColorScheme = if (darkTheme) DarkExtendedColorScheme else LightExtendedColorScheme

val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
Expand All @@ -67,9 +110,11 @@ fun CahierAppTheme(
}
}

MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
CompositionLocalProvider(LocalExtendedColorScheme provides extendedColorScheme) {
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
Expand All @@ -51,15 +56,18 @@ internal fun BrushSliderControl(
label: String,
value: Float,
valueRange: ClosedFloatingPointRange<Float>,
onValueChange: (Float) -> Unit
onValueChange: (Float) -> Unit,
) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = label, style = MaterialTheme.typography.bodyMedium)
Text(text = String.format(java.util.Locale.US, "%.2f", value), style = MaterialTheme.typography.bodySmall)
Text(
text = String.format(java.util.Locale.US, "%.2f", value),
style = MaterialTheme.typography.bodySmall
)
}
Slider(
value = value,
Expand All @@ -76,7 +84,7 @@ internal fun BrushSliderControl(
fun CustomColorPickerDialog(
initialColor: Color,
onColorSelected: (Color) -> Unit,
onDismissRequest: () -> Unit
onDismissRequest: () -> Unit,
) {
var currentColor by remember { mutableStateOf(HsvColor.from(initialColor)) }

Expand All @@ -99,7 +107,8 @@ fun CustomColorPickerDialog(
)
Spacer(modifier = Modifier.height(16.dp))

val hexString = String.format(java.util.Locale.US, "%08x", currentColor.toColor().toArgb())
val hexString =
String.format(java.util.Locale.US, "%08x", currentColor.toColor().toArgb())
Text(
text = hexString,
style = MaterialTheme.typography.bodyMedium,
Expand All @@ -124,3 +133,52 @@ fun CustomColorPickerDialog(
}
)
}

/**
* A generic [ExposedDropdownMenuBox] for selecting from enum-like value lists.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun <T> EnumDropdown(
label: String,
currentValue: T,
values: List<T>,
displayName: @Composable (T) -> String,
onSelected: (T) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }

ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it },
modifier = modifier
) {
OutlinedTextField(
value = displayName(currentValue),
onValueChange = {},
readOnly = true,
label = { Text(label) },
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
},
modifier = Modifier
.menuAnchor()
.fillMaxWidth()
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
values.forEach { value ->
DropdownMenuItem(
text = { Text(displayName(value)) },
onClick = {
onSelected(value)
expanded = false
}
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.example.cahier.R
import ink.proto.BrushBehavior
import ink.proto.PredefinedEasingFunction as ProtoPredefinedEasingFunction

/**
* Dispatches to the correct editor based on the [BrushBehavior.Node.NodeCase].
Expand Down Expand Up @@ -177,7 +178,7 @@ internal fun ResponseNodeEditor(
when (selected) {
ResponseCurveType.Predefined ->
newResponseBuilder.setPredefinedResponseCurve(
ink.proto.PredefinedEasingFunction.PREDEFINED_EASING_EASE
ProtoPredefinedEasingFunction.PREDEFINED_EASING_EASE
)
ResponseCurveType.CubicBezier ->
newResponseBuilder.setCubicBezierResponseCurve(
Expand Down Expand Up @@ -261,7 +262,7 @@ internal fun ResponseNodeEditor(
EnumDropdown(
label = stringResource(R.string.brush_designer_node_predefined_curve),
currentValue = response.predefinedResponseCurve,
values = ink.proto.PredefinedEasingFunction.entries.toList(),
values = ProtoPredefinedEasingFunction.entries.toList(),
displayName = {
it.name.replace("PREDEFINED_EASING_FUNCTION_", "")
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,18 @@ class NumericLimits(
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun NumericField(
modifier: Modifier = Modifier,
title: String,
value: Float,
limits: NumericLimits,
onValueChangeFinished: (() -> Unit)? = null,
onValueChanged: (Float) -> Unit
) {
val displayValue = limits.fromRealValue(value)
var showTextInput by remember { mutableStateOf(false) }
var textInputValue by remember { mutableStateOf("") }

Column(modifier = Modifier.padding(vertical = 4.dp)) {
Column(modifier = modifier.padding(vertical = 4.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
Expand Down Expand Up @@ -191,7 +193,8 @@ internal fun NumericField(
value = displayValue.coerceIn(limits.min, limits.max),
onValueChange = { onValueChanged(limits.toRealValue(it)) },
valueRange = limits.min..limits.max,
modifier = Modifier.weight(1f)
modifier = Modifier.weight(1f),
onValueChangeFinished = onValueChangeFinished
)

IconButton(onClick = {
Expand Down
Loading