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 @@ -42,6 +42,11 @@ import androidx.compose.ui.unit.dp
import com.example.cahier.R
import com.godaddy.android.colorpicker.ClassicColorPicker
import com.godaddy.android.colorpicker.HsvColor
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.DropdownMenuItem

/**
* A reusable slider control for brush property editing.
Expand Down Expand Up @@ -124,3 +129,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>,
modifier: Modifier = Modifier,
displayName: @Composable (T) -> String,
onSelected: (T) -> Unit
) {
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 @@ -174,7 +175,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 @@ -258,7 +259,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
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import com.example.cahier.R
import ink.proto.BrushFamily as ProtoBrushFamily
import ink.proto.BrushPaint as ProtoBrushPaint
import ink.proto.ColorFunction as ProtoColorFunction
import ink.proto.Color as ProtoColor

/**
* Tab 1: Paint & texture controls — multi-layer textures, multi-function colors,
Expand Down Expand Up @@ -438,7 +439,7 @@ private fun ColorFunctionEditor(
1 -> onFunctionChanged(
ProtoColorFunction.newBuilder()
.setReplaceColor(
ink.proto.Color.getDefaultInstance()
ProtoColor.getDefaultInstance()
)
.build()
)
Expand Down Expand Up @@ -477,52 +478,7 @@ private fun ColorFunctionEditor(
}
}

/**
* 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: (T) -> String,
onSelected: (T) -> Unit
) {
var expanded by remember { mutableStateOf(false) }

ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it }
) {
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
}
)
}
}
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,17 @@ object BrushFamilyConverter {
.build()
}

fun createCoat(
coatNode: GraphNode,
graph: BrushGraph,
behaviorCache: MutableMap<String, List<List<ink.proto.BrushBehavior.Node>>> = mutableMapOf()
): ProtoBrushCoat {
val nodesById = graph.nodes.associateBy { it.id }
val edgesByToNode = graph.edges.filter { !it.isDisabled }.groupBy { it.toNodeId }
val context = ConversionContext(graph, nodesById, edgesByToNode, behaviorCache)
return createCoat(coatNode, context)
}

private fun createCoat(
coatNode: GraphNode,
context: ConversionContext
Expand Down
Loading