| /* |
| * Copyright 2022 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.material3 |
| |
| import androidx.compose.animation.core.MutableTransitionState |
| 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.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 |
| |
| /** |
| * <a href="https://m3.material.io/components/menus/overview" class="external" target="_blank">Material Design dropdown menu</a>. |
| * |
| * Menus display a list of choices on a temporary surface. They appear when users interact with a |
| * button, action, or other control. |
| * |
| * ![Dropdown menu image](https://developer.android.com/images/reference/androidx/compose/material3/menu.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.material3.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 |
| */ |
| @Suppress("ModifierParameter") |
| @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 |
| ) { |
| 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, |
| modifier = modifier, |
| content = content |
| ) |
| } |
| } |
| } |
| |
| /** |
| * <a href="https://m3.material.io/components/menus/overview" class="external" target="_blank">Material Design dropdown menu</a> item. |
| * |
| * Menus display a list of choices on a temporary surface. They appear when users interact with a |
| * button, action, or other control. |
| * |
| * ![Dropdown menu image](https://developer.android.com/images/reference/androidx/compose/material3/menu.png) |
| * |
| * Example usage: |
| * @sample androidx.compose.material3.samples.MenuSample |
| * |
| * @param text The menu item text |
| * @param onClick Called when the menu item was clicked |
| * @param modifier The modifier to be applied to the menu item |
| * @param leadingIcon Optional leading icon to be displayed at the beginning of the item's text |
| * @param trailingIcon Optional trailing icon to be displayed at the end of the item's text. This |
| * trailing icon slot can also accept [Text] to indicate a keyboard shortcut, for example. |
| * @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 colors [MenuItemColors] that will be used to resolve the background and content color for |
| * this item in different states. See [MenuDefaults.itemColors]. |
| * @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( |
| text: @Composable () -> Unit, |
| onClick: () -> Unit, |
| modifier: Modifier = Modifier, |
| leadingIcon: @Composable (() -> Unit)? = null, |
| trailingIcon: @Composable (() -> Unit)? = null, |
| enabled: Boolean = true, |
| colors: MenuItemColors = MenuDefaults.itemColors(), |
| contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding, |
| interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, |
| ) { |
| DropdownMenuItemContent( |
| text = text, |
| onClick = onClick, |
| modifier = modifier, |
| leadingIcon = leadingIcon, |
| trailingIcon = trailingIcon, |
| enabled = enabled, |
| colors = colors, |
| contentPadding = contentPadding, |
| interactionSource = interactionSource, |
| ) |
| } |