跳转到内容

WindowIconDropdownMenu

WindowIconDropdownMenu 是基于 IconButton 的封装,点击图标按钮后会展开 WindowDropdownPopup(通过 Dialog 在窗口级别渲染)。适用于工具栏的 action 槽位,例如 TopAppBar 的右侧动作按钮——单个图标按钮展开后呈现一组动作、排序选项或筛选开关。与 OverlayIconDropdownMenu 不同,它不需要 Scaffold

引入

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

基本用法

WindowIconDropdownMenu 放在 TopAppBar(或 SmallTopAppBar)的 actions 槽中,点击图标即可展开弹出框。这是最典型的使用场景——工具栏上的一个图标按钮,展开后呈现一组菜单项。与 OverlayIconDropdownMenu 不同,菜单本身不要求外层有 Scaffold,但承载 TopAppBar 的最自然方式仍然是 Scaffold

kotlin
val entry = DropdownEntry(
    items = listOf("编辑", "复制", "分享", "删除").map { text ->
        DropdownItem(text = text, onClick = { /* 处理动作 */ })
    }
)

Scaffold(
    topBar = {
        SmallTopAppBar(
            title = "收件箱",
            actions = {
                WindowIconDropdownMenu(entry = entry) {
                    Icon(imageVector = COUIIcons.Edit, contentDescription = "动作菜单")
                }
            }
        )
    }
) { padding ->
    // 页面内容
}

排序 / 单选

对于排序菜单或单选场景,在每个 DropdownItem 上设置 selected,并保持 collapseOnSelection = true(entry 重载的默认值),让弹出框在每次选中后自动关闭。

kotlin
var sortIndex by remember { mutableStateOf(0) }
val entry = DropdownEntry(
    items = listOf("名称", "日期", "大小").mapIndexed { index, text ->
        DropdownItem(text = text, selected = sortIndex == index, onClick = { sortIndex = index })
    }
)

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

多选

用一个 Set 跟踪选中值,在每个条目的 onClick 中切换状态,并设置 collapseOnSelection = false 让弹出框在多次选择之间保持打开。

kotlin
var selected by remember { mutableStateOf(setOf("照片")) }
val entry = DropdownEntry(
    items = listOf("照片", "视频", "文件").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 = "多选")
}

分组菜单

传入 entries: List<DropdownEntry> 即可显示由分割线隔开的多个分组。

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

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

带提示槽的选项

hint 槽渲染在标题区块与选中指示图标之间,最大宽度 40dp,适合放红点、计数徽标或极短标签。与 ColorOS 一致,行被禁用时提示槽整体隐藏。

kotlin
val entry = DropdownEntry(
    items = listOf(
        DropdownItem(text = "Inbox", hint = { Badge(count = 12) }),
        DropdownItem(text = "Updates", hint = { Badge() }),
        // 该行被禁用,因此徽标不会显示。
        DropdownItem(text = "Archive", hint = { Badge(count = 3) }, enabled = false),
    )
)

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

分组标题

DropdownEntry 可声明 title,渲染为该分组各项之上的不可点击标题行(12sp 中等字重、次级标签 色、最多 2 行)。分组标题与已有的分组分割线可以共存。

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 = true 可将某项标记为危险操作,其标题使用错误色而非常规标签色;被禁用的警示项仍回退 到禁用色。

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

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

组件状态

禁用状态

kotlin
WindowIconDropdownMenu(
    entry = DropdownEntry(items = listOf(DropdownItem(text = "选项 1"))),
    enabled = false
) {
    Icon(imageVector = COUIIcons.MoreCircle, contentDescription = "更多")
}

当所有 DropdownEntry 都不包含任何条目时,菜单也会被隐式禁用。

属性

WindowIconDropdownMenu 属性(Entries 重载)

属性名类型说明默认值是否必须
entriesList<DropdownEntry>由分割线隔开的下拉选项分组-
modifierModifier应用于外层 Box 的修饰符Modifier
enabledBoolean图标按钮是否可交互true
maxHeightDp?下拉菜单的最大高度null
dropdownColorsDropdownColors下拉选项的颜色配置DropdownDefaults.dropdownColors()
collapseOnSelectionBoolean每次选中后是否关闭弹出框entries.size <= 1
onExpandedChange((Boolean) -> Unit)?展开状态变化时的回调null
backgroundColorColor底层 IconButton 的背景颜色Color.Unspecified
cornerRadiusDp底层 IconButton 的圆角半径IconButtonDefaults.CornerRadius
minHeightDp底层 IconButton 的最小高度IconButtonDefaults.MinHeight
minWidthDp底层 IconButton 的最小宽度IconButtonDefaults.MinWidth
content@Composable () -> Unit按钮内显示的图标(或其他可组合内容)-

Entry 重载属性

属性名类型说明默认值是否必须
entryDropdownEntry单个下拉选项分组-
collapseOnSelectionBoolean选中后是否关闭弹出框true

其余参数与上方 entries 重载完全一致。

属性名类型说明默认值是否必须
itemsList<DropdownItem>此分组中显示的条目-
enabledBoolean此分组是否启用。为 false 时禁用整组条目;为 true 时仍会遵循每个条目的 enabled 状态true
titleString?可选的不可点击分组标题,渲染在各项之上(12sp 中等字重、次级标签色、最多 2 行)null
属性名类型说明默认值是否必须
textString选项显示的文本-
enabledBoolean选项是否可点击,禁用时置灰true
selectedBoolean选项是否处于选中状态false
onClick(() -> Unit)?点击选项时触发的回调null
icon@Composable ((Modifier) -> Unit)?显示在选项文本前的图标null
summaryString?显示在选项文本下方的摘要文本null
childrenList<DropdownItem>?可选的子菜单项;仅级联变体null
hint@Composable (() -> Unit)?可选的尾部提示槽(徽标、红点、短计数),显示在选中指示图标之前,最大宽度 40dp。行被禁用时整体隐藏null
alertBoolean是否为警示(危险)项;其标题使用错误色false
属性名类型说明
contentColorColor选项标题颜色
summaryColorColor选项摘要颜色
containerColorColor选项背景颜色
selectedContentColorColor选中项标题颜色
selectedSummaryColorColor选中项摘要颜色
selectedContainerColorColor选中项背景颜色
selectedIndicatorColorColor选中指示图标颜色
disabledContentColorColor禁用项标题颜色
alertContentColorColor警示项标题颜色
headerColorColor分组标题行的标题颜色

变更日志

基于 Apache-2.0 许可发布