Skip to content

WindowIconCascadingDropdownMenu

WindowIconCascadingDropdownMenu is an IconButton wrapper that opens a WindowCascadingListPopup (rendered at window level via Dialog) when clicked. Unlike OverlayIconCascadingDropdownMenu, it does not require a Scaffold. Items whose DropdownItem.children is non-empty become submenu triggers; cascading depth is limited to 2.

Import

kotlin
import io.github.suqi8.coui.kmp.menu.WindowIconCascadingDropdownMenu
import io.github.suqi8.coui.kmp.basic.DropdownEntry
import io.github.suqi8.coui.kmp.basic.DropdownItem

Basic Usage

Place WindowIconCascadingDropdownMenu in the actions slot of a TopAppBar (or SmallTopAppBar). Top-level items with non-empty children render a chevron and expand inline on click. No Scaffold is required around the menu itself, but a Scaffold is still the natural way to host a TopAppBar.

kotlin
var sortIndex by remember { mutableStateOf(0) }
var viewIndex by remember { mutableStateOf(0) }
var filterIndex by remember { mutableStateOf(0) }

val sortLabels = listOf("Sort by capture date", "Sort by date added")
val viewLabels = listOf("Group by date", "Compact")
val filterLabels = listOf("All items", "Camera album")

val entries = listOf(
    DropdownEntry(
        items = sortLabels.mapIndexed { idx, label ->
            DropdownItem(
                text = label,
                selected = sortIndex == idx,
                onClick = { sortIndex = idx },
            )
        },
    ),
    DropdownEntry(
        items = listOf(
            DropdownItem(
                text = "View mode",
                children = viewLabels.mapIndexed { idx, label ->
                    DropdownItem(
                        text = label,
                        selected = viewIndex == idx,
                        onClick = { viewIndex = idx },
                    )
                },
            ),
            DropdownItem(
                text = "Filter",
                children = filterLabels.mapIndexed { idx, label ->
                    DropdownItem(
                        text = label,
                        selected = filterIndex == idx,
                        onClick = { filterIndex = idx },
                    )
                },
            ),
        ),
    ),
)

Scaffold(
    topBar = {
        SmallTopAppBar(
            title = "Library",
            actions = {
                WindowIconCascadingDropdownMenu(entries = entries) {
                    Icon(imageVector = COUIIcons.Tune, contentDescription = "Adjust")
                }
            }
        )
    }
) { padding ->
    // page content
}

Single Entry Overload

When you only need one group of items, use the entry overload to skip the surrounding listOf(...).

kotlin
val entry = DropdownEntry(
    items = listOf(
        DropdownItem(
            text = "View mode",
            children = listOf(
                DropdownItem(text = "Group by date", onClick = { /* ... */ }),
                DropdownItem(text = "Compact", onClick = { /* ... */ }),
            ),
        ),
        DropdownItem(text = "Refresh", onClick = { /* ... */ }),
    ),
)

WindowIconCascadingDropdownMenu(entry = entry) {
    Icon(imageVector = COUIIcons.Tune, contentDescription = "Adjust")
}

Component States

Disabled State

kotlin
WindowIconCascadingDropdownMenu(
    entry = DropdownEntry(items = listOf(DropdownItem(text = "Option 1"))),
    enabled = false,
) {
    Icon(imageVector = COUIIcons.MoreCircle, contentDescription = "More")
}

The menu is also implicitly disabled when no DropdownEntry contains any items.

Cascading Depth

Cascading depth is capped at 2. Items at the secondary level cannot have their own children; deeper trees are silently ignored.

Properties

WindowIconCascadingDropdownMenu Properties (Entries Overload)

Property NameTypeDescriptionDefault ValueRequired
entriesList<DropdownEntry>Dropdown entry groups; top-level items with non-empty children become submenu triggers-Yes
modifierModifierModifier applied to the wrapping BoxModifierNo
enabledBooleanWhether the icon button is interactivetrueNo
maxHeightDp?Maximum height of either side of the cascading popupnullNo
dropdownColorsDropdownColorsColor configuration for dropdown itemsDropdownDefaults.dropdownColors()No
collapseOnSelectionBooleanWhether to close the popup after a leaf is selectedtrueNo
onExpandedChange((Boolean) -> Unit)?Callback when the expanded state changesnullNo
backgroundColorColorBackground color of the underlying IconButtonColor.UnspecifiedNo
cornerRadiusDpCorner radius of the underlying IconButtonIconButtonDefaults.CornerRadiusNo
minHeightDpMinimum height of the underlying IconButtonIconButtonDefaults.MinHeightNo
minWidthDpMinimum width of the underlying IconButtonIconButtonDefaults.MinWidthNo
content@Composable () -> UnitThe icon (or other composable) shown inside the button-Yes

Entry Overload Properties

Property NameTypeDescriptionDefault ValueRequired
entryDropdownEntrySingle dropdown entry group-Yes
collapseOnSelectionBooleanWhether to close the popup after a leaf is selectedtrueNo

All other parameters are identical to the entries overload above.

Property NameTypeDescriptionDefault ValueRequired
itemsList<DropdownItem>Items shown in this dropdown group-Yes
enabledBooleanWhether this group is enabled. False disables all items; true still respects each item's enabled statetrueNo
titleString?Optional non-clickable group header rendered above the items (12sp medium, secondary label, max 2 lines)nullNo
Property NameTypeDescriptionDefault ValueRequired
textStringText shown for the item-Yes
enabledBooleanWhether the item can be clicked. Disabled items are graytrueNo
selectedBooleanWhether the item is selectedfalseNo
onClick(() -> Unit)?Callback invoked when the item is clicked. Ignored when children is non-empty (the click expands the submenu instead)nullNo
icon@Composable ((Modifier) -> Unit)?Icon shown before the item textnullNo
summaryString?Summary text shown below the item textnullNo
childrenList<DropdownItem>?Optional submenu items; only the cascading variants render these as a submenu (depth limited to 2)nullNo
hint@Composable (() -> Unit)?Optional trailing hint slot (badge, red dot, short count) shown before the selection indicator, width-capped at 40dp. Hidden entirely while the row is disablednullNo
alertBooleanWhether this is an alert (destructive) item; its title uses the error colorfalseNo
Property NameTypeDescription
contentColorColorColor of the option title
summaryColorColorColor of the option summary
containerColorColorBackground color of the option
selectedContentColorColorTitle color of the selected option
selectedSummaryColorColorSummary color of the selected option
selectedContainerColorColorBackground color of the selected option
selectedIndicatorColorColorColor of the selected indicator icon
disabledContentColorColorTitle color of a disabled option
alertContentColorColorTitle color of an alert option
headerColorColorTitle color of a group header row

Changelog

Released under the Apache-2.0 License