Skip to content

WindowIconDropdownMenu

WindowIconDropdownMenu is an IconButton wrapper that opens a WindowDropdownPopup (rendered at window level via Dialog) when clicked. It is intended for toolbar action slots, such as the actions area of TopAppBar, where a single icon needs to expand into a list of actions, sort options, or filter toggles. Unlike OverlayIconDropdownMenu, it does not require a Scaffold.

Import

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

Basic Usage

Place WindowIconDropdownMenu in the actions slot of a TopAppBar (or SmallTopAppBar). Clicking the icon opens the popup. This is the typical use case — a toolbar action button that expands into a list of menu items. Unlike OverlayIconDropdownMenu, no Scaffold is required around the menu itself, but a Scaffold is still the natural way to host a TopAppBar.

kotlin
val entry = DropdownEntry(
    items = listOf("Edit", "Duplicate", "Share", "Delete").map { text ->
        DropdownItem(text = text, onClick = { /* handle action */ })
    }
)

Scaffold(
    topBar = {
        SmallTopAppBar(
            title = "Inbox",
            actions = {
                WindowIconDropdownMenu(entry = entry) {
                    Icon(imageVector = COUIIcons.Edit, contentDescription = "Action menu")
                }
            }
        )
    }
) { padding ->
    // page content
}

Sort / Single Select

For sort menus or radio-style choices, set selected on each DropdownItem and let collapseOnSelection = true (default for the entry overload) close the popup after each pick.

kotlin
var sortIndex by remember { mutableStateOf(0) }
val entry = DropdownEntry(
    items = listOf("Name", "Date", "Size").mapIndexed { index, text ->
        DropdownItem(text = text, selected = sortIndex == index, onClick = { sortIndex = index })
    }
)

WindowIconDropdownMenu(entry = entry) {
    Icon(imageVector = COUIIcons.Sort, contentDescription = "Sort")
}

Multi Select

Track a Set of selected values, toggle each item from onClick, and pass collapseOnSelection = false so the popup stays open between picks.

kotlin
var selected by remember { mutableStateOf(setOf("Photos")) }
val entry = DropdownEntry(
    items = listOf("Photos", "Videos", "Files").map { text ->
        DropdownItem(
            text = text,
            selected = text in selected,
            onClick = {
                selected = if (text in selected) selected - text else selected + text
            }
        )
    }
)

WindowIconDropdownMenu(entry = entry, collapseOnSelection = false) {
    Icon(imageVector = COUIIcons.SelectAll, contentDescription = "Multiple selection")
}

Grouped Menu

Pass entries: List<DropdownEntry> to render multiple groups separated by dividers.

kotlin
val entries = listOf(
    DropdownEntry(items = listOf("Item A-1", "Item A-2").map { DropdownItem(text = it) }),
    DropdownEntry(items = listOf("Item B-1", "Item B-2", "Item B-3").map { DropdownItem(text = it) })
)

WindowIconDropdownMenu(entries = entries) {
    Icon(imageVector = COUIIcons.MoreCircle, contentDescription = "More")
}

Items with a Hint Slot

The hint slot renders between the title block and the selection indicator, capped at 40dp wide. It suits a red dot, a count badge, or a very short label. Matching ColorOS, the hint is hidden entirely while the row is disabled.

kotlin
val entry = DropdownEntry(
    items = listOf(
        DropdownItem(text = "Inbox", hint = { Badge(count = 12) }),
        DropdownItem(text = "Updates", hint = { Badge() }),
        // The badge is suppressed because the row is disabled.
        DropdownItem(text = "Archive", hint = { Badge(count = 3) }, enabled = false),
    )
)

Scaffold {
    WindowIconDropdownMenu(entry = entry) {
        Icon(imageVector = COUIIcons.More, contentDescription = "More")
    }
}

Group Headers

A DropdownEntry can declare a title, rendered above its items as a non-clickable header row (12sp medium, secondary label color, at most 2 lines). Headers coexist with the group divider that already separates adjacent entries.

kotlin
val entries = listOf(
    DropdownEntry(
        title = "Sort by",
        items = listOf("Name", "Date modified").map { DropdownItem(text = it) }
    ),
    DropdownEntry(
        title = "Order",
        items = listOf("Ascending", "Descending").map { DropdownItem(text = it) }
    )
)

Scaffold {
    WindowIconDropdownMenu(entries = entries) {
        Icon(imageVector = COUIIcons.Sort, contentDescription = "Sort")
    }
}

Alert Items

Set alert = true to mark a destructive action. Its title uses the error color instead of the normal label color; disabled alert rows still fall back to the disabled color.

kotlin
val entry = DropdownEntry(
    items = listOf(
        DropdownItem(text = "Rename"),
        DropdownItem(text = "Delete", alert = true),
    )
)

Scaffold {
    WindowIconDropdownMenu(entry = entry) {
        Icon(imageVector = COUIIcons.More, contentDescription = "More")
    }
}

Component States

Disabled State

kotlin
WindowIconDropdownMenu(
    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.

Properties

WindowIconDropdownMenu Properties (Entries Overload)

Property NameTypeDescriptionDefault ValueRequired
entriesList<DropdownEntry>Dropdown entry groups separated by dividers-Yes
modifierModifierModifier applied to the wrapping BoxModifierNo
enabledBooleanWhether the icon button is interactivetrueNo
maxHeightDp?Maximum height of the dropdown popupnullNo
dropdownColorsDropdownColorsColor configuration for dropdown itemsDropdownDefaults.dropdownColors()No
collapseOnSelectionBooleanWhether to close the popup after each selectionentries.size <= 1No
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 selectiontrueNo

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 clickednullNo
icon@Composable ((Modifier) -> Unit)?Icon shown before the item textnullNo
summaryString?Summary text shown below the item textnullNo
childrenList<DropdownItem>?Optional submenu items; cascading variants onlynullNo
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