kotlin 使用垂直滚动条合成桌面下拉菜单

c0vxltue  于 2023-06-24  发布在  Kotlin
关注(0)|答案(1)|浏览(140)

我从KotlinCompose for Desktop中获取了一个下拉菜单,我想包含一个垂直滚动条。下拉菜单的源代码在这里。他们有一个sample,工作正常,但我不能让它显示垂直滚动条。默认情况下不显示。
还有一个VerticalScrollbar示例,我也尝试过,但我还没有让它与DropdownMenu一起工作。
verticalScroll()放入DropdownMenu(Modifier)中会导致错误Vertically scrolled component was measured with an infinity maximum height constraints, which is disallowed...
DropdownMenu下面添加VerticalScrollbar()会导致错误Can't represent a size of 1073741824 in Constraints
所以,据我所知,在DropdownMenuPopup或类似的东西中,有一些东西让我很难做到这一点。
有没有一种方法可以实现可视滚动条?到目前为止我的布局是这样的。(您可以 * 滚动 * 向下到下面的下拉菜单...)

data class Lookup(val id: String, val name: String)

fun main() = application {
   Window(
      onCloseRequest = ::exitApplication,
      state = rememberWindowState(width = 1280.dp, height = 800.dp)
   ) {
      MaterialTheme {
         Scaffold {
            Column(
               modifier = Modifier
                  .fillMaxSize()
                  .verticalScroll(rememberScrollState()),
               horizontalAlignment = Alignment.CenterHorizontally
            ) {
               val lookups = LookupService.getLookups() // about 75 items
               val (expanded, setExpanded) = remember { mutableStateOf(false) }
               val (selected, setSelected) = remember { mutableStateOf<Lookup?>(null) }

               Spacer(Modifier.height(20.dp))
               Box(Modifier.wrapContentSize(Alignment.TopStart)) {
                  val icon = if (expanded) {
                     Icons.Filled.KeyboardArrowUp
                  } else {
                     Icons.Filled.KeyboardArrowDown
                  }
                  OutlinedTextField(
                     value = selected?.name ?: "",
                     onValueChange = { },
                     modifier = Modifier
                        .width(360.dp)
                        .onKeyEvent {
                           if (it.key == Key.DirectionDown && !expanded) {
                              setExpanded(true)
                              return@onKeyEvent true
                           }
                           return false
                        }
                        .clickable { setExpanded(true) },
                     singleLine = true,
                     label = { Text("Select an item") },
                     trailingIcon = {
                        Icon(icon, "Select an item", Modifier.clickable {
                           setExpanded(!expanded)
                        })
                     },
                     enabled = expanded,
                     colors = TextFieldDefaults.textFieldColors(
                        disabledTextColor = LocalContentColor.current.copy(LocalContentAlpha.current),
                        backgroundColor = Color.Transparent
                     )
                  )

                  DropdownMenu( // Desktop version, so it creates a "Popup" per the source code
                     expanded = expanded,
                     onDismissRequest = {
                        runBlocking { // to handle a glitch where the dropdown may "unexpand & expand" again on clicking
                           delay(200)
                           setExpanded(false)
                        }
                     },
                     modifier = Modifier
                        .width(360.dp)
                        .background(Color.White)
                        .clip(RoundedCornerShape(5.dp))
                     // SHOULD HAVE VERTICAL SCROLLBAR SHOW UP AS PART OF THIS DROPDOWNMENU COLUMN
                  ) {
                     lookups.forEach { lookup -> 
                        DropdownMenuItem(
                           onClick = {
                              setExpanded(false)
                              setSelected(lookup)
                           }
                        ) {
                           Text(lookup.name)
                        }
                     }
                  }
               }
            }
         }
      }
   }
}
bejyjqdl

bejyjqdl1#

我在目前公开的问题Scrollbar doesn't work for DropdownMenu #587中找到了答案。
这就是我如何让我的滚动条在桌面版本的下拉菜单中工作。
1.将此版本的DesktopMenu.desktop.kt复制到项目中。
1.将此版本的Menu.kt复制到项目中。
1.将此卡片添加到您的Menu.kt中,环绕显示内容的Column
1.根据问题中的这条评论,将其添加到第3点的Card中。

Box(
    modifier = modifier
        .width(IntrinsicSize.Max)
) {
    val scrollState = rememberScrollState()
    var columnSize by remember { mutableStateOf<IntSize?>(null) }
    Column(
        modifier = Modifier
            .padding(vertical = DropdownMenuVerticalPadding)
            .verticalScroll(scrollState)
            .onSizeChanged { size ->
                columnSize = size
            },
        content = content
    )
    columnSize?.let { size ->
        VerticalScrollbar(
            modifier = Modifier
                .align(Alignment.CenterEnd)
                .height(with(LocalDensity.current) { size.height.toDp() }),
            scrollState = scrollState
        )
    }
}

1.最后一点是,它会溢出到我的屏幕上方,所以我回到了当前版本的DesktopMenu.desktop.kt,将这个版本的DesktopDropdownMenuPositionProvider放回我的DesktopMenu.desktop.kt中。

/**
 * Positions a dropdown relative to another widget (its anchor).
 */
@Immutable
internal data class DesktopDropdownMenuPositionProvider(
    val contentOffset: DpOffset,
    val density: Density,
    val onPositionCalculated: (IntRect, IntRect) -> Unit = { _, _ -> }
) : PopupPositionProvider {
    override fun calculatePosition(
        anchorBounds: IntRect,
        windowSize: IntSize,
        layoutDirection: LayoutDirection,
        popupContentSize: IntSize
    ): IntOffset {

        val isLtr = layoutDirection == LayoutDirection.Ltr

        // Coerce such that this..this+size fits into min..max; if impossible, align with min
        fun Int.coerceWithSizeIntoRangePreferMin(size: Int, min: Int, max: Int) = when {
            this < min -> min
            this + size > max -> max - size
            else -> this
        }

        // Coerce such that this..this+size fits into min..max; if impossible, align with max
        fun Int.coerceWithSizeIntoRangePreferMax(size: Int, min: Int, max: Int) = when {
            this + size > max -> max - size
            this < min -> min
            else -> this
        }

        fun Int.coerceWithSizeIntoRange(size: Int, min: Int, max: Int) = when {
            isLtr -> coerceWithSizeIntoRangePreferMin(size, min, max)
            else -> coerceWithSizeIntoRangePreferMax(size, min, max)
        }

        // The min margin above and below the menu, relative to the screen.
        val verticalMargin = with(density) { MenuVerticalMargin.roundToPx() }
        // The content offset specified using the dropdown offset parameter.
        val contentOffsetX = with(density) { contentOffset.x.roundToPx() }
        val contentOffsetY = with(density) { contentOffset.y.roundToPx() }

        // Compute horizontal position.
        val preferredX = if (isLtr) {
            anchorBounds.left + contentOffsetX
        }
        else {
            anchorBounds.right - contentOffsetX - popupContentSize.width
        }
        val x = preferredX.coerceWithSizeIntoRange(
            size = popupContentSize.width,
            min = 0,
            max = windowSize.width
        )

        // Compute vertical position.
        val toBottom = maxOf(anchorBounds.bottom + contentOffsetY, verticalMargin)
        val toTop = anchorBounds.top - contentOffsetY - popupContentSize.height
        val toCenter = anchorBounds.top - popupContentSize.height / 2
        val toWindowBottom = windowSize.height - popupContentSize.height - verticalMargin
        var y = sequenceOf(toBottom, toTop, toCenter, toWindowBottom).firstOrNull {
            it >= verticalMargin &&
                it + popupContentSize.height <= windowSize.height - verticalMargin
        } ?: toTop

        // Desktop specific vertical position checking
        val aboveAnchor = anchorBounds.top + contentOffsetY
        val belowAnchor = windowSize.height - anchorBounds.bottom - contentOffsetY

        if (belowAnchor >= aboveAnchor) {
            y = anchorBounds.bottom + contentOffsetY
        }

        if (y + popupContentSize.height > windowSize.height) {
            y = windowSize.height - popupContentSize.height
        }

        y = y.coerceAtLeast(0)

        onPositionCalculated(
            anchorBounds,
            IntRect(x, y, x + popupContentSize.width, y + popupContentSize.height)
        )
        return IntOffset(x, y)
    }
}

相关问题