@@ -46,11 +46,10 @@ import androidx.compose.foundation.text.BasicTextField
4646import androidx.compose.foundation.verticalScroll
4747import androidx.compose.material3.ExperimentalMaterial3Api
4848import androidx.compose.material3.LocalContentColor
49+ import androidx.compose.material3.MaterialTheme
4950import androidx.compose.material3.MaterialTheme.colorScheme
5051import androidx.compose.material3.MaterialTheme.typography
5152import androidx.compose.material3.ProvideTextStyle
52- import androidx.compose.material3.ShapeDefaults.ExtraSmall
53- import androidx.compose.material3.Surface
5453import androidx.compose.material3.Text
5554import androidx.compose.material3.TextField
5655import androidx.compose.material3.TextFieldDefaults
@@ -78,6 +77,7 @@ import androidx.compose.ui.geometry.Offset
7877import androidx.compose.ui.geometry.Offset.Companion.Zero
7978import androidx.compose.ui.graphics.Color
8079import androidx.compose.ui.graphics.Color.Companion.Transparent
80+ import androidx.compose.ui.graphics.Shape
8181import androidx.compose.ui.input.key.Key.Companion.DirectionDown
8282import androidx.compose.ui.input.key.Key.Companion.DirectionUp
8383import androidx.compose.ui.input.key.Key.Companion.Escape
@@ -112,7 +112,9 @@ import io.spine.chords.core.keyboard.KeyModifiers.Companion.Shift
112112import io.spine.chords.core.keyboard.KeyRange
113113import io.spine.chords.core.keyboard.key
114114import io.spine.chords.core.keyboard.matches
115+ import io.spine.chords.core.layout.PopupSurface
115116import io.spine.chords.core.primitive.VerticalScrollbar
117+ import io.spine.chords.core.styling.ChordsTheme
116118import java.awt.event.KeyEvent.CHAR_UNDEFINED
117119import java.lang.Character.UnicodeBlock
118120import java.lang.Character.UnicodeBlock.SPECIALS
@@ -288,6 +290,41 @@ public class DropdownListBox<I> : Component() {
288290 */
289291 public var unfocusInvoker: (() -> Unit )? = null
290292
293+ /* *
294+ * Minimum item height, or `null` to use the current Chords theme value.
295+ */
296+ public var itemMinHeight: Dp ? by mutableStateOf(null )
297+
298+ /* *
299+ * Vertical content padding, or `null` to use the current Chords theme value.
300+ */
301+ public var listContentPadding: Dp ? by mutableStateOf(null )
302+
303+ /* *
304+ * The popup shape, or `null` to use the current Material small shape.
305+ */
306+ public var listShape: Shape ? by mutableStateOf(null )
307+
308+ /* *
309+ * The popup's tonal elevation.
310+ */
311+ public var listTonalElevation: Dp by mutableStateOf(0 .dp)
312+
313+ /* *
314+ * The popup's shadow elevation.
315+ */
316+ public var listShadowElevation: Dp by mutableStateOf(8 .dp)
317+
318+ /* *
319+ * Selected item background, or `null` to use the theme selection color.
320+ */
321+ public var selectedItemColor: Color ? by mutableStateOf(null )
322+
323+ /* *
324+ * Keyboard-preselected item background, or `null` to use the theme hover color.
325+ */
326+ public var preselectedItemColor: Color ? by mutableStateOf(null )
327+
291328 /* *
292329 * A density of the screen, it is used when calculating which part
293330 * of drop-down list should be visible to user.
@@ -384,11 +421,6 @@ public class DropdownListBox<I> : Component() {
384421 */
385422 private var totalItemsHeight by mutableStateOf(0 .dp)
386423
387- /* *
388- * Vertical padding of drop-down list content.
389- */
390- private val listVerticalPadding = 8 .dp
391-
392424 /* *
393425 * Heights of none item in drop-down list.
394426 */
@@ -867,7 +899,10 @@ public class DropdownListBox<I> : Component() {
867899 * to be used.
868900 */
869901 @Composable
902+ @Suppress(" LongMethod" ) // Keeps selection ordering and item rendering in one list pass.
870903 private fun BoxScope.DropdownListContent () {
904+ val resolvedItemMinHeight =
905+ itemMinHeight ? : ChordsTheme .dimensions.dropdownItemHeight
871906 Column (
872907 modifier = Modifier
873908 .width(MinWidth )
@@ -883,10 +918,13 @@ public class DropdownListBox<I> : Component() {
883918 DropdownListNoneItem (
884919 text = noneItemText,
885920 color = if (preselectedItemIndex == - 1 ) {
886- colorScheme.primary.copy(alpha = 0.1f )
921+ preselectedItemColor ? : colorScheme.primary.copy(
922+ alpha = ChordsTheme .interaction.hoveredStateAlpha
923+ )
887924 } else {
888925 null
889926 },
927+ itemMinHeight = resolvedItemMinHeight,
890928 onMeasureHeight = { measuredHeight ->
891929 noneItemHeight = measuredHeight
892930 },
@@ -898,11 +936,13 @@ public class DropdownListBox<I> : Component() {
898936 items.forEachIndexed { index, item ->
899937 val color = when (index) {
900938 selectedItemIndex -> {
901- colorScheme.primary.copy(alpha = 0.2f )
939+ selectedItemColor ? : colorScheme.primaryContainer
902940 }
903941
904942 preselectedItemIndex -> {
905- colorScheme.primary.copy(alpha = 0.1f )
943+ preselectedItemColor ? : colorScheme.primary.copy(
944+ alpha = ChordsTheme .interaction.hoveredStateAlpha
945+ )
906946 }
907947
908948 else -> {
@@ -915,13 +955,17 @@ public class DropdownListBox<I> : Component() {
915955 onMeasureHeight = { measuredHeight ->
916956 itemHeights[index] = measuredHeight
917957 },
918- color = color
958+ color = color,
959+ itemMinHeight = resolvedItemMinHeight
919960 ) {
920961 itemContent(item)
921962 }
922963 }
923964 } else {
924- DropdownListNoItems (content = noItemsContent)
965+ DropdownListNoItems (
966+ itemMinHeight = resolvedItemMinHeight,
967+ content = noItemsContent
968+ )
925969 }
926970 }
927971 VerticalScrollbar (scrollState) {
@@ -961,13 +1005,18 @@ public class DropdownListBox<I> : Component() {
9611005 properties = PopupProperties (focusable = searchSelectionEnabled),
9621006 onPreviewKeyEvent = { handleKeyEventWhenDropdownExpanded(it) }
9631007 ) {
964- Surface (shape = ExtraSmall , tonalElevation = 3.0 .dp, shadowElevation = 3.0 .dp) {
1008+ val contentPadding = listContentPadding ? : ChordsTheme .dimensions.spacingXSmall
1009+ PopupSurface (
1010+ shape = listShape ? : MaterialTheme .shapes.small,
1011+ tonalElevation = listTonalElevation,
1012+ shadowElevation = listShadowElevation
1013+ ) {
9651014 visibleListHeight = min(
966- totalItemsHeight, listAvailableHeight - listVerticalPadding * 2
1015+ totalItemsHeight, listAvailableHeight - contentPadding * 2
9671016 )
9681017 Box (
9691018 modifier = Modifier
970- .padding(vertical = listVerticalPadding )
1019+ .padding(vertical = contentPadding )
9711020 .height(visibleListHeight)
9721021 ) {
9731022 if (scrollPositionRequested != null ) {
@@ -1160,6 +1209,8 @@ private class DropdownListBoxScopeImpl(
11601209 * callback that is invoked when item is positioned.
11611210 * @param color
11621211 * the background color of drop-down list item.
1212+ * @param itemMinHeight
1213+ * the minimum height of the item.
11631214 * @param content
11641215 * content to be displayed inside drop-down list item.
11651216 */
@@ -1168,6 +1219,7 @@ private fun DropdownListItem(
11681219 onClick : () -> Unit ,
11691220 onMeasureHeight : (Int ) -> Unit ,
11701221 color : Color ? ,
1222+ itemMinHeight : Dp ,
11711223 content : @Composable () -> Unit
11721224) {
11731225 val itemHeight = remember { mutableStateOf(0 ) }
@@ -1179,7 +1231,7 @@ private fun DropdownListItem(
11791231 onClick = onClick
11801232 )
11811233 .fillMaxWidth()
1182- .heightIn(48 .dp )
1234+ .heightIn(itemMinHeight )
11831235 .onGloballyPositioned {
11841236 val height = it.size.height
11851237 if (height != itemHeight.value) {
@@ -1197,22 +1249,29 @@ private fun DropdownListItem(
11971249/* *
11981250 * The drop-down list without items.
11991251 *
1252+ * @param itemMinHeight
1253+ * the minimum height of the item.
12001254 * @param content
12011255 * the content to be shown when drop-down list doesn't have any items.
12021256 */
12031257@Composable
1204- private fun DropdownListNoItems (content : @Composable (() -> Unit )) {
1258+ private fun DropdownListNoItems (
1259+ itemMinHeight : Dp ,
1260+ content : @Composable (() -> Unit )
1261+ ) {
12051262 Row (
12061263 modifier = Modifier
12071264 .fillMaxWidth()
1208- .heightIn(48 .dp )
1209- .padding(horizontal = 12 .dp )
1265+ .heightIn(itemMinHeight )
1266+ .padding(horizontal = ChordsTheme .dimensions.spacingMedium )
12101267 .background(Transparent ),
12111268 verticalAlignment = CenterVertically ,
12121269 horizontalArrangement = Center
12131270 ) {
12141271 StyledContent (
1215- contentColor = colorScheme.secondary.copy(alpha = 0.5f ),
1272+ contentColor = colorScheme.onSurfaceVariant.copy(
1273+ alpha = ChordsTheme .interaction.disabledContentAlpha
1274+ ),
12161275 textStyle = typography.titleSmall,
12171276 content = content
12181277 )
@@ -1226,6 +1285,8 @@ private fun DropdownListNoItems(content: @Composable (() -> Unit)) {
12261285 * the text to be displayed for drop-down list none item.
12271286 * @param color
12281287 * the background color of drop-down list none item.
1288+ * @param itemMinHeight
1289+ * the minimum height of the item.
12291290 * @param onMeasureHeight
12301291 * callback that is invoked when item is positioned.
12311292 * @param onClick
@@ -1235,6 +1296,7 @@ private fun DropdownListNoItems(content: @Composable (() -> Unit)) {
12351296private fun DropdownListNoneItem (
12361297 text : String = "<None >",
12371298 color : Color ? = null,
1299+ itemMinHeight : Dp ,
12381300 onMeasureHeight : (Int ) -> Unit ,
12391301 onClick : () -> Unit
12401302) {
@@ -1254,16 +1316,20 @@ private fun DropdownListNoneItem(
12541316 }
12551317 }
12561318 .fillMaxWidth()
1257- .heightIn(48 .dp )
1319+ .heightIn(itemMinHeight )
12581320 .background(color ? : Transparent ),
12591321 verticalAlignment = CenterVertically
12601322 ) {
12611323 StyledContent (
1262- contentColor = colorScheme.secondary.copy(alpha = 0.5f ),
1324+ contentColor = colorScheme.onSurfaceVariant.copy(
1325+ alpha = ChordsTheme .interaction.disabledContentAlpha
1326+ ),
12631327 content = {
12641328 Text (
12651329 text = text,
1266- modifier = Modifier .padding(horizontal = 12 .dp)
1330+ modifier = Modifier .padding(
1331+ horizontal = ChordsTheme .dimensions.spacingMedium
1332+ )
12671333 )
12681334 }
12691335 )
0 commit comments