Skip to content

Commit ef6def4

Browse files
authored
Merge pull request #41 from PureSwift/feature/tint
2 parents 88094fe + 63a52f9 commit ef6def4

4 files changed

Lines changed: 58 additions & 2 deletions

File tree

AndroidSwiftUICore/Sources/AndroidSwiftUICore/Modifiers/AppearanceModifiers.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,20 @@ public extension View {
8080
modifier(_ClipShapeModifier(kind: shape._shapeKind, cornerRadius: shape._cornerRadius))
8181
}
8282
}
83+
84+
// MARK: - Tint
85+
86+
/// Sets the accent color for controls in the subtree (an environment value,
87+
/// like `.foregroundColor`).
88+
public struct _TintModifier: RenderModifier {
89+
let color: Color
90+
public var _modifierNode: ModifierNode {
91+
ModifierNode(kind: "tint", args: ["color": color.propValue])
92+
}
93+
}
94+
95+
public extension View {
96+
func tint(_ color: Color) -> ModifiedContent<Self, _TintModifier> {
97+
modifier(_TintModifier(color: color))
98+
}
99+
}

AndroidSwiftUICore/Tests/AndroidSwiftUICoreTests/EvaluatorTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ struct ModifierTests {
227227
let node = ViewHost(Text("x").shadow(radius: 6)).evaluate()
228228
#expect(node.modifiers.first { $0.kind == "shadow" }?.args["radius"] == .double(6))
229229
}
230+
231+
@Test("tint emits its color")
232+
func tint() {
233+
let node = ViewHost(Text("x").tint(.green)).evaluate()
234+
#expect(node.modifiers.first { $0.kind == "tint" }?.args["color"] == Color.green.propValue)
235+
}
230236
}
231237

232238
// MARK: - Graphics

Demo/App.swiftpm/Sources/StylePlaygrounds.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,20 @@ import SwiftUI
66

77
struct StylePlayground: View {
88
@State private var taps = 0
9+
@State private var toggle = true
10+
@State private var slider = 0.5
911
var body: some View {
1012
ScrollView {
1113
VStack(alignment: .leading, spacing: 0) {
14+
Example("Inherited tint") {
15+
VStack(alignment: .leading, spacing: 10) {
16+
Button("Tinted button") { taps += 1 }
17+
Toggle("Tinted toggle", isOn: $toggle)
18+
Slider(value: $slider, in: 0...1)
19+
ProgressView(value: slider)
20+
}
21+
.tint(.pink)
22+
}
1223
Example("Inherited font") {
1324
VStack(alignment: .leading, spacing: 6) {
1425
Text("All three")

Demo/swiftui/src/commonMain/kotlin/com/pureswift/swiftui/Render.kt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
4444
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
4545
import androidx.compose.material3.AlertDialog
4646
import androidx.compose.material3.Button
47+
import androidx.compose.material3.ButtonDefaults
4748
import androidx.compose.material3.CircularProgressIndicator
49+
import androidx.compose.material3.ProgressIndicatorDefaults
50+
import androidx.compose.material3.SliderDefaults
51+
import androidx.compose.material3.SwitchDefaults
4852
import androidx.compose.material3.DatePicker
4953
import androidx.compose.material3.DatePickerDialog
5054
import androidx.compose.material3.rememberDatePickerState
@@ -150,6 +154,7 @@ internal val LocalInheritedFontSize = compositionLocalOf { TextUnit.Unspecified
150154
internal val LocalInheritedFontWeight = compositionLocalOf<FontWeight?> { null }
151155
internal val LocalInheritedColor = compositionLocalOf { Color.Unspecified }
152156
internal val LocalInheritedDisabled = compositionLocalOf { false }
157+
internal val LocalTint = compositionLocalOf<Color?> { null }
153158

154159
/// Interprets a Swift-evaluated node tree into Material 3 composables.
155160
///
@@ -180,6 +185,7 @@ internal fun RenderChild(node: ViewNode) {
180185
var fontWeight = LocalInheritedFontWeight.current
181186
var color = LocalInheritedColor.current
182187
var disabled = LocalInheritedDisabled.current
188+
var tint = LocalTint.current
183189
for (m in node.modifiers) {
184190
when (m.kind) {
185191
"font" -> {
@@ -193,6 +199,7 @@ internal fun RenderChild(node: ViewNode) {
193199
"fontWeight" -> m.args.string("weight")?.let { fontWeight = fontWeightFor(it) }
194200
"foregroundColor" -> m.args.long("color")?.let { color = Color(it.toInt()) }
195201
"disabled" -> if ((m.args["value"] as? kotlinx.serialization.json.JsonPrimitive)?.content == "true") disabled = true
202+
"tint" -> m.args.long("color")?.let { tint = Color(it.toInt()) }
196203
}
197204
}
198205

@@ -202,6 +209,7 @@ internal fun RenderChild(node: ViewNode) {
202209
LocalInheritedFontWeight provides fontWeight,
203210
LocalInheritedColor provides color,
204211
LocalInheritedDisabled provides disabled,
212+
LocalTint provides tint,
205213
) { RenderResolved(node) }
206214
}
207215

@@ -214,10 +222,12 @@ private fun RenderResolved(node: ViewNode) {
214222

215223
"Button" -> {
216224
val onTap = node.long("onTap")
225+
val tint = LocalTint.current
217226
Button(
218227
// own disabled and any inherited `.disabled` both apply
219228
onClick = { onTap?.let { SwiftBridge.sink.invokeVoid(it) } },
220229
enabled = !node.isDisabled() && !LocalInheritedDisabled.current,
230+
colors = if (tint != null) ButtonDefaults.buttonColors(containerColor = tint) else ButtonDefaults.buttonColors(),
221231
modifier = node.composeModifiers(),
222232
) {
223233
RenderChildren(node)
@@ -226,11 +236,13 @@ private fun RenderResolved(node: ViewNode) {
226236

227237
"Toggle" -> {
228238
val onChange = node.long("onChange")
239+
val tint = LocalTint.current
229240
Row(verticalAlignment = Alignment.CenterVertically, modifier = node.composeModifiers()) {
230241
RenderChildren(node)
231242
Switch(
232243
checked = node.bool("isOn") ?: false,
233244
onCheckedChange = { onChange?.let { id -> SwiftBridge.sink.invokeBool(id, it) } },
245+
colors = if (tint != null) SwitchDefaults.colors(checkedTrackColor = tint) else SwitchDefaults.colors(),
234246
)
235247
}
236248
}
@@ -313,21 +325,31 @@ private fun RenderResolved(node: ViewNode) {
313325

314326
"ProgressView" -> {
315327
val value = node.double("value")
328+
val tint = LocalTint.current
316329
if (value != null) {
317-
LinearProgressIndicator(progress = { value.toFloat() }, modifier = node.composeModifiers().fillMaxWidth())
330+
LinearProgressIndicator(
331+
progress = { value.toFloat() },
332+
color = tint ?: ProgressIndicatorDefaults.linearColor,
333+
modifier = node.composeModifiers().fillMaxWidth(),
334+
)
318335
} else {
319-
CircularProgressIndicator(modifier = node.composeModifiers())
336+
CircularProgressIndicator(
337+
color = tint ?: ProgressIndicatorDefaults.circularColor,
338+
modifier = node.composeModifiers(),
339+
)
320340
}
321341
}
322342

323343
"Slider" -> {
324344
val onChange = node.long("onChange")
325345
val min = (node.double("min") ?: 0.0).toFloat()
326346
val max = (node.double("max") ?: 1.0).toFloat()
347+
val tint = LocalTint.current
327348
Slider(
328349
value = (node.double("value") ?: 0.0).toFloat(),
329350
onValueChange = { onChange?.let { id -> SwiftBridge.sink.invokeDouble(id, it.toDouble()) } },
330351
valueRange = min..max,
352+
colors = if (tint != null) SliderDefaults.colors(thumbColor = tint, activeTrackColor = tint) else SliderDefaults.colors(),
331353
modifier = node.composeModifiers().fillMaxWidth(),
332354
)
333355
}

0 commit comments

Comments
 (0)