@@ -100,7 +100,9 @@ import androidx.compose.foundation.shape.CircleShape
100100import androidx.compose.ui.text.font.FontStyle
101101import androidx.compose.ui.text.font.FontWeight
102102import androidx.compose.ui.text.style.TextAlign
103+ import androidx.compose.ui.text.input.PasswordVisualTransformation
103104import androidx.compose.ui.text.input.TextFieldValue
105+ import androidx.compose.ui.text.input.VisualTransformation
104106import androidx.compose.ui.unit.TextUnit
105107import androidx.compose.ui.unit.sp
106108import androidx.compose.ui.unit.IntOffset
@@ -223,6 +225,10 @@ fun Render(node: ViewNode) {
223225
224226 " TextField" -> RenderTextField (node)
225227
228+ " Stepper" -> RenderStepper (node)
229+
230+ " Menu" -> RenderMenu (node)
231+
226232 " Picker" -> RenderPicker (node)
227233
228234 " NavStack" -> RenderNavStack (node)
@@ -289,10 +295,47 @@ private fun RenderTextField(node: ViewNode) {
289295 }
290296 },
291297 label = { Text (node.string(" placeholder" ) ? : " " ) },
298+ visualTransformation = if (node.bool(" secure" ) == true ) PasswordVisualTransformation () else VisualTransformation .None ,
292299 modifier = node.composeModifiers().fillMaxWidth(),
293300 )
294301}
295302
303+ // Label, then − / + edge buttons pushed to the trailing side.
304+ @Composable
305+ private fun RenderStepper (node : ViewNode ) {
306+ val onIncrement = node.long(" onIncrement" )
307+ val onDecrement = node.long(" onDecrement" )
308+ Row (verticalAlignment = Alignment .CenterVertically , modifier = node.composeModifiers().fillMaxWidth()) {
309+ RenderChildren (node)
310+ Spacer (modifier = Modifier .weight(1f ))
311+ TextButton (onClick = { onDecrement?.let { SwiftBridge .sink.invokeVoid(it) } }) { Text (" −" , fontSize = 20 .sp) }
312+ TextButton (onClick = { onIncrement?.let { SwiftBridge .sink.invokeVoid(it) } }) { Text (" +" , fontSize = 20 .sp) }
313+ }
314+ }
315+
316+ // A trigger button opening a dropdown; each child Button becomes a menu item
317+ // firing its own tap callback.
318+ @Composable
319+ private fun RenderMenu (node : ViewNode ) {
320+ var expanded by remember { mutableStateOf(false ) }
321+ Box {
322+ TextButton (onClick = { expanded = true }) { Text (node.string(" label" ) ? : " " ) }
323+ DropdownMenu (expanded = expanded, onDismissRequest = { expanded = false }) {
324+ for (child in node.children) {
325+ if (child.isPresentation()) continue
326+ val onTap = child.long(" onTap" )
327+ DropdownMenuItem (
328+ text = { Text (pickerLabel(child)) },
329+ onClick = {
330+ expanded = false
331+ onTap?.let { SwiftBridge .sink.invokeVoid(it) }
332+ },
333+ )
334+ }
335+ }
336+ }
337+ }
338+
296339@Composable
297340private fun RenderPicker (node : ViewNode ) {
298341 val onChange = node.long(" onChange" )
0 commit comments