/* * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package androidx.compose.material import androidx.compose.animation.core.MutableTransitionState import androidx.compose.foundation.ScrollState import androidx.compose.foundation.interaction.Interaction import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.rememberScrollState import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupProperties /** * Material Design dropdown menu. * * A dropdown menu is a compact way of displaying multiple choices. It appears upon interaction with * an element (such as an icon or button) or when users perform a specific action. * * ![Menus image](https://developer.android.com/images/reference/androidx/compose/material/menus.png) * * A [DropdownMenu] behaves similarly to a [Popup], and will use the position of the parent layout * to position itself on screen. Commonly a [DropdownMenu] will be placed in a [Box] with a sibling * that will be used as the 'anchor'. Note that a [DropdownMenu] by itself will not take up any * space in a layout, as the menu is displayed in a separate window, on top of other content. * * The [content] of a [DropdownMenu] will typically be [DropdownMenuItem]s, as well as custom * content. Using [DropdownMenuItem]s will result in a menu that matches the Material * specification for menus. Also note that the [content] is placed inside a scrollable [Column], * so using a [LazyColumn] as the root layout inside [content] is unsupported. * * [onDismissRequest] will be called when the menu should close - for example when there is a * tap outside the menu, or when the back key is pressed. * * [DropdownMenu] changes its positioning depending on the available space, always trying to be * fully visible. It will try to expand horizontally, depending on layout direction, to the end of * its parent, then to the start of its parent, and then screen end-aligned. Vertically, it will * try to expand to the bottom of its parent, then from the top of its parent, and then screen * top-aligned. An [offset] can be provided to adjust the positioning of the menu for cases when * the layout bounds of its parent do not coincide with its visual bounds. Note the offset will * be applied in the direction in which the menu will decide to expand. * * Example usage: * @sample androidx.compose.material.samples.MenuSample * * @param expanded Whether the menu is currently open and visible to the user * @param onDismissRequest Called when the user requests to dismiss the menu, such as by * tapping outside the menu's bounds * @param offset [DpOffset] to be added to the position of the menu */ @Deprecated( level = DeprecationLevel.HIDDEN, replaceWith = ReplaceWith( expression = "DropdownMenu(expanded,onDismissRequest, modifier, offset, " + "rememberScrollState(), properties, content)", "androidx.compose.foundation.rememberScrollState" ), message = "Replaced by a DropdownMenu function with a ScrollState parameter" ) @Composable fun DropdownMenu( expanded: Boolean, onDismissRequest: () -> Unit, modifier: Modifier = Modifier, offset: DpOffset = DpOffset(0.dp, 0.dp), properties: PopupProperties = PopupProperties(focusable = true), content: @Composable ColumnScope.() -> Unit ) = DropdownMenu( expanded = expanded, onDismissRequest = onDismissRequest, modifier = modifier, offset = offset, scrollState = rememberScrollState(), properties = properties, content = content ) /** * Material Design dropdown menu. * * A dropdown menu is a compact way of displaying multiple choices. It appears upon interaction with * an element (such as an icon or button) or when users perform a specific action. * * ![Menus image](https://developer.android.com/images/reference/androidx/compose/material/menus.png) * * A [DropdownMenu] behaves similarly to a [Popup], and will use the position of the parent layout * to position itself on screen. Commonly a [DropdownMenu] will be placed in a [Box] with a sibling * that will be used as the 'anchor'. Note that a [DropdownMenu] by itself will not take up any * space in a layout, as the menu is displayed in a separate window, on top of other content. * * The [content] of a [DropdownMenu] will typically be [DropdownMenuItem]s, as well as custom * content. Using [DropdownMenuItem]s will result in a menu that matches the Material * specification for menus. Also note that the [content] is placed inside a scrollable [Column], * so using a [LazyColumn] as the root layout inside [content] is unsupported. * * [onDismissRequest] will be called when the menu should close - for example when there is a * tap outside the menu, or when the back key is pressed. * * [DropdownMenu] changes its positioning depending on the available space, always trying to be * fully visible. It will try to expand horizontally, depending on layout direction, to the end of * its parent, then to the start of its parent, and then screen end-aligned. Vertically, it will * try to expand to the bottom of its parent, then from the top of its parent, and then screen * top-aligned. An [offset] can be provided to adjust the positioning of the menu for cases when * the layout bounds of its parent do not coincide with its visual bounds. Note the offset will * be applied in the direction in which the menu will decide to expand. * * Example usage: * @sample androidx.compose.material.samples.MenuSample * * Example usage with a [ScrollState] to control the menu items scroll position: * @sample androidx.compose.material.samples.MenuWithScrollStateSample * * @param expanded whether the menu is expanded or not * @param onDismissRequest called when the user requests to dismiss the menu, such as by tapping * outside the menu's bounds * @param modifier [Modifier] to be applied to the menu's content * @param offset [DpOffset] to be added to the position of the menu * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling * @param properties [PopupProperties] for further customization of this popup's behavior * @param content the content of this dropdown menu, typically a [DropdownMenuItem] */ @Composable fun DropdownMenu( expanded: Boolean, onDismissRequest: () -> Unit, modifier: Modifier = Modifier, offset: DpOffset = DpOffset(0.dp, 0.dp), scrollState: ScrollState = rememberScrollState(), properties: PopupProperties = PopupProperties(focusable = true), content: @Composable ColumnScope.() -> Unit ) { val expandedStates = remember { MutableTransitionState(false) } expandedStates.targetState = expanded if (expandedStates.currentState || expandedStates.targetState) { val transformOriginState = remember { mutableStateOf(TransformOrigin.Center) } val density = LocalDensity.current val popupPositionProvider = DropdownMenuPositionProvider( offset, density ) { parentBounds, menuBounds -> transformOriginState.value = calculateTransformOrigin(parentBounds, menuBounds) } Popup( onDismissRequest = onDismissRequest, popupPositionProvider = popupPositionProvider, properties = properties ) { DropdownMenuContent( expandedStates = expandedStates, transformOriginState = transformOriginState, scrollState = scrollState, modifier = modifier, content = content ) } } } /** * Material Design dropdown menu item. * * * Example usage: * @sample androidx.compose.material.samples.MenuSample * * @param onClick Called when the menu item was clicked * @param modifier The modifier to be applied to the menu item * @param enabled Controls the enabled state of the menu item - when `false`, the menu item * will not be clickable and [onClick] will not be invoked * @param contentPadding the padding applied to the content of this menu item * @param interactionSource the [MutableInteractionSource] representing the stream of * [Interaction]s for this DropdownMenuItem. You can create and pass in your own remembered * [MutableInteractionSource] if you want to observe [Interaction]s and customize the * appearance / behavior of this DropdownMenuItem in different [Interaction]s. */ @Composable fun DropdownMenuItem( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { DropdownMenuItemContent( onClick = onClick, modifier = modifier, enabled = enabled, contentPadding = contentPadding, interactionSource = interactionSource, content = content ) }