Travis Griggs
03/31/2023, 10:20 PMTravis Griggs
03/31/2023, 10:24 PMonGloballyPositioned
modifier to capture where the original label is at
• Populate the full contents of the dialog so I could use Column/Spacer layout to place the dialog text field in the same place
• Then emulate the dismiss click (including suppress the ripple), since there was no scrim anymore
• Use onGloballyPositioned on the TextField as a hook to trigger focus so the keyboard raises
I would love feedback, redirection, on any of these. I'm curious if there's a easier general methodology for temporarily restricting a interaction to a subset of the screen so it can be temporarily modal-ish.Travis Griggs
03/31/2023, 10:24 PMprivate fun NameRow(mc: MCHost) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp)
) {
var isEditing by remember { mutableStateOf(false) }
// value to retain where the name label ended up at
var nameTop by remember { mutableStateOf(0.dp) }
// capture density here in composition, because we can't run it in the globally positioned closure
var density = LocalDensity.current
SettingsLabel(text = "Name", modifier = Modifier.align(Alignment.CenterStart))
Text(text = mc.name ?: "", modifier = Modifier
.align(Alignment.Center)
// capture the root position in nameTop
.onGloballyPositioned { layoutCoordinates ->
nameTop = with(density) { layoutCoordinates.positionInRoot().y.toDp() }
})
CircleButton(vectorID = R.drawable.screwdriver_in_circle_mask,
modifier = Modifier.align(Alignment.CenterEnd),
click = { isEditing = isEditing.not() })
if (isEditing) {
var editedName by remember {
mutableStateOf(
TextFieldValue(
// force the cursor to the end
text = mc.name!!, selection = TextRange(mc.name!!.length)
)
)
}
val focusRequester = remember { FocusRequester() }
AlertDialog(onDismissRequest = { isEditing = false }) {
// The dialog wants to minimially center things
// Fill it with a column
Column(
modifier = Modifier
.fillMaxHeight()
// But now there's no dismiss area, so we have to emulate the dismiss click here
.clickable(
// use the version that allows us to suppress the ripple so it looks like a normal dialog dismiss
MutableInteractionSource(),
indication = null,
onClick = { isEditing = false })
) {
// The full dialog column matches the root context we capture the nameTop in, add 4 for padding and insert a spacer
Spacer(modifier = Modifier.height(nameTop - 4.dp))
BasicTextField(
value = editedName,
onValueChange = { newValue ->
editedName = newValue
},
modifier = Modifier
.padding(horizontal = 40.dp)
.fillMaxWidth()
.background(Color.White, shape = RoundedCornerShape(25))
.padding(vertical = 4.dp)
.focusRequester(focusRequester)
// suggested on SO as a reliable way get this TextField focused when the dialog opens
.onGloballyPositioned { focusRequester.requestFocus() }, // IMPORTANT
textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Words,
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Default
),
keyboardActions = KeyboardActions(onDone = {
mc.name = editedName.text
isEditing = false
}),
singleLine = true,
)
}
}
}
}
}
Jan
04/12/2023, 9:28 PM