Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 26b900e1 authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Do not expand when SettingsDropdownBox disabled" into main

parents 0758f426 4803b1d5
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsDropdownBoxPageProvider
import com.android.settingslib.spa.gallery.editor.SettingsDropdownCheckBoxProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.itemList.ItemListPageProvider
@@ -99,7 +99,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
                OperateListPageProvider,
                EditorMainPageProvider,
                SettingsOutlinedTextFieldPageProvider,
                SettingsExposedDropdownMenuBoxPageProvider,
                SettingsDropdownBoxPageProvider,
                SettingsDropdownCheckBoxProvider,
                SettingsTextFieldPasswordPageProvider,
                SearchScaffoldPageProvider,
+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ object EditorMainPageProvider : SettingsPageProvider {
        return listOf(
            SettingsOutlinedTextFieldPageProvider.buildInjectEntry().setLink(fromPage = owner)
                .build(),
            SettingsExposedDropdownMenuBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
            SettingsDropdownBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
                .build(),
            SettingsDropdownCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
                .build(),
+40 −15
Original line number Diff line number Diff line
@@ -28,16 +28,15 @@ import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
import com.android.settingslib.spa.widget.editor.SettingsDropdownBox
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold

private const val TITLE = "Sample SettingsExposedDropdownMenuBox"
private const val TITLE = "Sample SettingsDropdownBox"

object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {
    override val name = "SettingsExposedDropdownMenuBox"
    private const val exposedDropdownMenuBoxLabel = "ExposedDropdownMenuBoxLabel"
object SettingsDropdownBoxPageProvider : SettingsPageProvider {
    override val name = "SettingsDropdownBox"

    override fun getTitle(arguments: Bundle?): String {
        return TITLE
@@ -45,16 +44,42 @@ object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {

    @Composable
    override fun Page(arguments: Bundle?) {
        var selectedItem by remember { mutableIntStateOf(-1) }
        val options = listOf("item1", "item2", "item3")
        RegularScaffold(title = TITLE) {
            SettingsExposedDropdownMenuBox(
                label = exposedDropdownMenuBoxLabel,
                options = options,
            Regular()
            NotEnabled()
            Empty()
        }
    }

    @Composable
    private fun Regular() {
        var selectedItem by remember { mutableIntStateOf(-1) }
        SettingsDropdownBox(
            label = "SettingsDropdownBox",
            options = listOf("item1", "item2", "item3"),
            selectedOptionIndex = selectedItem,
        ) { selectedItem = it }
    }

    @Composable
    private fun NotEnabled() {
        var selectedItem by remember { mutableIntStateOf(0) }
        SettingsDropdownBox(
            label = "Not enabled",
            options = listOf("item1", "item2", "item3"),
            enabled = false,
            selectedOptionIndex = selectedItem,
                enabled = true,
                onselectedOptionTextChange = { selectedItem = it })
        ) { selectedItem = it }
    }

    @Composable
    private fun Empty() {
        var selectedItem by remember { mutableIntStateOf(-1) }
        SettingsDropdownBox(
            label = "Empty",
            options = emptyList(),
            selectedOptionIndex = selectedItem,
        ) { selectedItem = it }
    }

    fun buildInjectEntry(): SettingsEntryBuilder {
@@ -70,8 +95,8 @@ object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {

@Preview(showBackground = true)
@Composable
private fun SettingsExposedDropdownMenuBoxPagePreview() {
private fun SettingsDropdownBoxPagePreview() {
    SettingsTheme {
        SettingsExposedDropdownMenuBoxPageProvider.Page(null)
        SettingsDropdownBoxPageProvider.Page(null)
    }
}
+32 −55
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ package com.android.settingslib.spa.widget.editor
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
@@ -31,80 +30,58 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme

@Composable
internal interface DropdownTextBoxScope {
    fun dismiss()
}

@OptIn(ExperimentalMaterial3Api::class)
fun SettingsExposedDropdownMenuBox(
@Composable
internal fun DropdownTextBox(
    label: String,
    options: List<String>,
    selectedOptionIndex: Int,
    enabled: Boolean,
    onselectedOptionTextChange: (Int) -> Unit,
    text: String,
    enabled: Boolean = true,
    errorMessage: String? = null,
    content: @Composable DropdownTextBoxScope.() -> Unit,
) {
    var expanded by remember { mutableStateOf(false) }
    val scope = remember {
        object : DropdownTextBoxScope {
            override fun dismiss() {
                expanded = false
            }
        }
    }
    ExposedDropdownMenuBox(
        expanded = expanded,
        onExpandedChange = { expanded = it },
        onExpandedChange = { expanded = enabled && it },
        modifier = Modifier
            .width(350.dp)
            .padding(SettingsDimension.menuFieldPadding),
            .padding(SettingsDimension.menuFieldPadding)
            .width(Width),
    ) {
        OutlinedTextField(
            // The `menuAnchor` modifier must be passed to the text field for correctness.
            modifier = Modifier
                .menuAnchor()
                .fillMaxWidth(),
            value = options.getOrElse(selectedOptionIndex) { "" },
            value = text,
            onValueChange = { },
            label = { Text(text = label) },
            trailingIcon = {
                ExposedDropdownMenuDefaults.TrailingIcon(
                    expanded = expanded
                )
            },
            trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
            singleLine = true,
            readOnly = true,
            enabled = enabled
            enabled = enabled,
            isError = errorMessage != null,
            supportingText = errorMessage?.let { { Text(text = it) } },
        )
        if (options.isNotEmpty()) {
        ExposedDropdownMenu(
            expanded = expanded,
                modifier = Modifier
                    .fillMaxWidth(),
            modifier = Modifier.width(Width),
            onDismissRequest = { expanded = false },
            ) {
                options.forEach { option ->
                    DropdownMenuItem(
                        text = { Text(option) },
                        onClick = {
                            onselectedOptionTextChange(options.indexOf(option))
                            expanded = false
                        },
                        contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
                    )
                }
            }
        }
        ) { scope.content() }
    }
}

@Preview
@Composable
private fun SettingsExposedDropdownMenuBoxsPreview() {
    val item1 = "item1"
    val item2 = "item2"
    val item3 = "item3"
    val options = listOf(item1, item2, item3)
    SettingsTheme {
        SettingsExposedDropdownMenuBox(
            label = "ExposedDropdownMenuBoxLabel",
            options = options,
            selectedOptionIndex = 0,
            enabled = true,
            onselectedOptionTextChange = {})
    }
}
 No newline at end of file
private val Width = 310.dp
+69 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settingslib.spa.widget.editor

import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.theme.SettingsTheme

@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun SettingsDropdownBox(
    label: String,
    options: List<String>,
    selectedOptionIndex: Int,
    enabled: Boolean = true,
    onSelectedOptionChange: (Int) -> Unit,
) {
    DropdownTextBox(
        label = label,
        text = options.getOrElse(selectedOptionIndex) { "" },
        enabled = enabled && options.isNotEmpty(),
    ) {
        options.forEachIndexed { index, option ->
            DropdownMenuItem(
                text = { Text(option) },
                onClick = {
                    dismiss()
                    onSelectedOptionChange(index)
                },
                contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
            )
        }
    }
}

@Preview
@Composable
private fun SettingsDropdownBoxPreview() {
    val item1 = "item1"
    val item2 = "item2"
    val item3 = "item3"
    val options = listOf(item1, item2, item3)
    SettingsTheme {
        SettingsDropdownBox(
            label = "ExposedDropdownMenuBoxLabel",
            options = options,
            selectedOptionIndex = 0,
            enabled = true,
        ) {}
    }
}
Loading