diff --git a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt index 8420db01c2c28..848c328d08052 100644 --- a/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt +++ b/compose/material/material/src/desktopMain/kotlin/androidx/compose/material/DesktopAlertDialog.desktop.kt @@ -46,11 +46,17 @@ import androidx.compose.ui.window.rememberDialogState import java.awt.event.KeyEvent import androidx.compose.ui.window.Dialog as CoreDialog import androidx.compose.foundation.background +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.type +/** + * The default padding for an [AlertDialog]. + */ +private val AlertDialogPadding = PaddingValues(24.dp) + /** * Alert dialog is a Dialog which interrupts the user with urgent information, details or actions. * @@ -92,6 +98,65 @@ fun AlertDialog( backgroundColor: Color = MaterialTheme.colors.surface, contentColor: Color = contentColorFor(backgroundColor), dialogProvider: AlertDialogProvider = PopupAlertDialogProvider +) { + AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = confirmButton, + modifier = modifier, + dismissButton = dismissButton, + title = title, + text = text, + shape = shape, + backgroundColor = backgroundColor, + contentColor = contentColor, + dialogPadding = AlertDialogPadding, + dialogProvider = dialogProvider + ) +} + +/** + * Alert dialog is a Dialog which interrupts the user with urgent information, details or actions. + * + * The dialog will position its buttons based on the available space. By default it will try to + * place them horizontally next to each other and fallback to horizontal placement if not enough + * space is available. There is also another version of this composable that has a slot for buttons + * to provide custom buttons layout. + * + * Sample of dialog: + * @sample androidx.compose.material.samples.AlertDialogSample + * + * @param onDismissRequest Callback that will be called when the user closes the dialog. + * @param confirmButton A button which is meant to confirm a proposed action, thus resolving + * what triggered the dialog. The dialog does not set up any events for this button so they need + * to be set up by the caller. + * @param modifier Modifier to be applied to the layout of the dialog. + * @param dismissButton A button which is meant to dismiss the dialog. The dialog does not set up + * any events for this button so they need to be set up by the caller. + * @param title The title of the Dialog which should specify the purpose of the Dialog. The title + * is not mandatory, because there may be sufficient information inside the [text]. Provided text + * style will be [Typography.subtitle1]. + * @param text The text which presents the details regarding the Dialog's purpose. Provided text + * style will be [Typography.body2]. + * @param shape Defines the Dialog's shape + * @param backgroundColor The background color of the dialog. + * @param contentColor The preferred content color provided by this dialog to its children. + * @param dialogPadding The outer padding of the dialog. + * @param dialogProvider Defines how to create dialog in which will be placed AlertDialog's content. + */ +@Composable +@ExperimentalMaterialApi +fun AlertDialog( + onDismissRequest: () -> Unit, + confirmButton: @Composable () -> Unit, + modifier: Modifier = Modifier, + dismissButton: @Composable (() -> Unit)? = null, + title: @Composable (() -> Unit)? = null, + text: @Composable (() -> Unit)? = null, + shape: Shape = MaterialTheme.shapes.medium, + backgroundColor: Color = MaterialTheme.colors.surface, + contentColor: Color = contentColorFor(backgroundColor), + dialogPadding: PaddingValues = AlertDialogPadding, + dialogProvider: AlertDialogProvider = PopupAlertDialogProvider, ) { AlertDialog( onDismissRequest = onDismissRequest, @@ -113,6 +178,54 @@ fun AlertDialog( shape = shape, backgroundColor = backgroundColor, contentColor = contentColor, + dialogPadding = dialogPadding, + dialogProvider = dialogProvider + ) +} + +/** + * Alert dialog is a Dialog which interrupts the user with urgent information, details or actions. + * + * This function can be used to fully customize the button area, e.g. with: + * + * @sample androidx.compose.material.samples.CustomAlertDialogSample + * + * @param onDismissRequest Callback that will be called when the user closes the dialog. + * @param buttons Function that emits the layout with the buttons. + * @param modifier Modifier to be applied to the layout of the dialog. + * @param title The title of the Dialog which should specify the purpose of the Dialog. The title + * is not mandatory, because there may be sufficient information inside the [text]. Provided text + * style will be [Typography.subtitle1]. + * @param text The text which presents the details regarding the Dialog's purpose. Provided text + * style will be [Typography.body2]. + * @param shape Defines the Dialog's shape. + * @param backgroundColor The background color of the dialog. + * @param contentColor The preferred content color provided by this dialog to its children. + * @param dialogProvider Defines how to create dialog in which will be placed AlertDialog's content. + */ +@Composable +@ExperimentalMaterialApi +fun AlertDialog( + onDismissRequest: () -> Unit, + buttons: @Composable () -> Unit, + modifier: Modifier = Modifier, + title: (@Composable () -> Unit)? = null, + text: @Composable (() -> Unit)? = null, + shape: Shape = MaterialTheme.shapes.medium, + backgroundColor: Color = MaterialTheme.colors.surface, + contentColor: Color = contentColorFor(backgroundColor), + dialogProvider: AlertDialogProvider = PopupAlertDialogProvider +) { + AlertDialog( + onDismissRequest = onDismissRequest, + buttons = buttons, + modifier = modifier, + title = title, + text = text, + shape = shape, + backgroundColor = backgroundColor, + contentColor = contentColor, + dialogPadding = AlertDialogPadding, dialogProvider = dialogProvider ) } @@ -135,6 +248,7 @@ fun AlertDialog( * @param shape Defines the Dialog's shape. * @param backgroundColor The background color of the dialog. * @param contentColor The preferred content color provided by this dialog to its children. + * @param dialogPadding The outer padding of the dialog. * @param dialogProvider Defines how to create dialog in which will be placed AlertDialog's content. */ @Composable @@ -148,17 +262,18 @@ fun AlertDialog( shape: Shape = MaterialTheme.shapes.medium, backgroundColor: Color = MaterialTheme.colors.surface, contentColor: Color = contentColorFor(backgroundColor), + dialogPadding: PaddingValues = AlertDialogPadding, dialogProvider: AlertDialogProvider = PopupAlertDialogProvider ) { with(dialogProvider) { AlertDialog( onDismissRequest = onDismissRequest, shape = shape, - modifier = modifier, + modifier = modifier.padding(dialogPadding), ) { modifier -> AlertDialogContent( buttons = buttons, - modifier = modifier.width(IntrinsicSize.Min), + modifier = modifier.width(IntrinsicSize.Max), title = title, text = text, shape = shape, diff --git a/compose/material/material/src/desktopTest/kotlin/androidx/compose/material/DesktopAlertDialogTest.kt b/compose/material/material/src/desktopTest/kotlin/androidx/compose/material/DesktopAlertDialogTest.kt index f8c54206af526..7db95c1528ed7 100644 --- a/compose/material/material/src/desktopTest/kotlin/androidx/compose/material/DesktopAlertDialogTest.kt +++ b/compose/material/material/src/desktopTest/kotlin/androidx/compose/material/DesktopAlertDialogTest.kt @@ -16,7 +16,7 @@ package androidx.compose.material -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.size import androidx.compose.material.internal.keyEvent import androidx.compose.runtime.CompositionLocalProvider @@ -27,14 +27,25 @@ import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.graphics.toPixelMap import androidx.compose.ui.input.key.Key import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.layout.IntrinsicMeasurable +import androidx.compose.ui.layout.IntrinsicMeasureScope +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasurePolicy +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag import androidx.compose.ui.renderComposeScene +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.assertWidthIsEqualTo import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performKeyPress +import androidx.compose.ui.test.runDesktopComposeUiTest +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp @@ -62,7 +73,7 @@ class DesktopAlertDialogTest { @OptIn(ExperimentalMaterialApi::class) AlertDialog( onDismissRequest = {}, - title = { Text("AlerDialog") }, + title = { Text("AlertDialog") }, text = { Text("Apply?") }, confirmButton = { Button(onClick = {}) { Text("Apply") } }, modifier = Modifier.size(dialogSize.width.dp, dialogSize.height.dp) @@ -86,7 +97,7 @@ class DesktopAlertDialogTest { @OptIn(ExperimentalMaterialApi::class) AlertDialog( onDismissRequest = { dismissCount++ }, - title = { Text("AlerDialog") }, + title = { Text("AlertDialog") }, text = { Text("Apply?") }, confirmButton = { Button(onClick = {}) { Text("Apply") } }, modifier = Modifier.size(dialogSize.width.dp, dialogSize.height.dp) @@ -119,14 +130,13 @@ class DesktopAlertDialogTest { // background. val screenshot = renderComposeScene(400, 400){ AlertDialog( - modifier = Modifier - .size(width = 400.dp, height = 100.dp) - .padding(horizontal = 150.dp), + modifier = Modifier.size(width = 400.dp, height = 100.dp), onDismissRequest = {}, title = {}, text = {}, dismissButton = {}, confirmButton = {}, + dialogPadding = PaddingValues(horizontal = 150.dp) ) } @@ -141,6 +151,43 @@ class DesktopAlertDialogTest { assertNotEquals(nearRealEdgePixel, backgroundPixel) } + @OptIn(ExperimentalTestApi::class, ExperimentalMaterialApi::class) + @Test + fun `alert dialog uses available width`() = runDesktopComposeUiTest( + width = 800, + height = 800 + ){ + setContent { + AlertDialog( + onDismissRequest = {}, + confirmButton = {}, + text = { + Layout( + modifier = Modifier.testTag("text_content"), + measurePolicy = object: MeasurePolicy { + override fun MeasureScope.measure( + measurables: List, + constraints: Constraints + ): MeasureResult { + val width = 200.coerceAtMost(constraints.maxWidth) + return layout(width, 200){} + } + + override fun IntrinsicMeasureScope.minIntrinsicWidth( + measurables: List, + height: Int + ): Int { + return 100 + } + } + ) + } + ) + } + + onNodeWithTag("text_content").assertWidthIsEqualTo(200.dp) + } + private fun calculateCenterPosition(rootSize: IntSize, childSize: IntSize): Offset { val x = (rootSize.width - childSize.width) / 2f val y = (rootSize.height - childSize.height) / 2f