diff --git a/python/python-sdk-configurator/frontend/src/components/ModuleList.kt b/python/python-sdk-configurator/frontend/src/components/ModuleList.kt index f3aae1f3cba5..c4f322475700 100644 --- a/python/python-sdk-configurator/frontend/src/components/ModuleList.kt +++ b/python/python-sdk-configurator/frontend/src/components/ModuleList.kt @@ -1,87 +1,54 @@ package com.intellij.python.sdkConfigurator.frontend.components +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.snapshots.SnapshotStateSet import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.rememberTextMeasurer -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.intellij.openapi.util.NlsSafe import com.intellij.python.sdkConfigurator.common.impl.CreateSdkDTO +import com.intellij.python.sdkConfigurator.common.impl.ModuleName import com.intellij.python.sdkConfigurator.common.impl.ToolIdDTO import com.intellij.python.sdkConfigurator.frontend.PySdkConfiguratorFrontendBundle import kotlinx.collections.immutable.ImmutableMap import org.jetbrains.annotations.Nls -import org.jetbrains.jewel.foundation.theme.JewelTheme -import org.jetbrains.jewel.ui.component.CheckboxRow -import org.jetbrains.jewel.ui.component.Icon -import org.jetbrains.jewel.ui.component.Text -import org.jetbrains.jewel.ui.component.VerticallyScrollableContainer +import org.jetbrains.jewel.ui.Orientation +import org.jetbrains.jewel.ui.component.* import org.jetbrains.jewel.ui.icon.IconKey @Composable internal fun ModuleList( - moduleItems: ImmutableMap, + moduleItems: ImmutableMap, icons: ImmutableMap, checked: SnapshotStateSet, - onCheckChange: (String, Boolean) -> Unit, + onCheck: (ModuleName) -> Unit, topLabel: @Nls String, projectStructureLabel: @Nls String, environmentLabel: @Nls String, ) { - val newText = remember { PySdkConfiguratorFrontendBundle.message("python.sdk.configurator.frontend.choose.modules.new") } - val ts = JewelTheme.defaultTextStyle - val padding = 2.dp - val longestItemChars = remember { (listOf(projectStructureLabel) + moduleItems.keys).maxBy { it.length } } - val leftColumnMinSize = measureText(longestItemChars, ts) - val spaceBetweenCols = 16.dp - Column(Modifier.width(800.dp).padding(padding), verticalArrangement = Arrangement.spacedBy(padding)) { - Text(topLabel, Modifier.padding(bottom = 10.dp)) - Row(Modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween) { - Text(projectStructureLabel, Modifier.width(leftColumnMinSize + spaceBetweenCols + padding + 26.dp)) //~ checkbox size - Text(environmentLabel, textAlign = TextAlign.End) - } - val moduleItems = remember { moduleItems.entries.sortedBy { it.key } } + val moduleItems = remember { moduleItems.entries.sortedBy { it.key } } + Box { VerticallyScrollableContainer { - Column(Modifier, horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(padding)) { + val colPadding = 2.dp + val horizontalArrangement = Arrangement.spacedBy(colPadding) + Column(Modifier.width(IntrinsicSize.Max).height(IntrinsicSize.Max).padding(end = scrollbarContentSafePadding())) { + Text(text = topLabel) + Divider(Orientation.Horizontal, thickness = 5.dp) + Row { + val modifier = Modifier.weight(1f) + Text(projectStructureLabel, modifier = modifier) + Divider(Orientation.Vertical) + Text(environmentLabel, modifier = modifier) + } + Divider(Orientation.Horizontal) for ((moduleName, moduleInfo) in moduleItems) { - val parent = when (moduleInfo) { - is CreateSdkDTO.ConfigurableModule -> null - is CreateSdkDTO.SameAs -> moduleInfo.parentModuleName - } - Row(Modifier.padding(padding), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(spaceBetweenCols)) { - val checked = moduleName in checked - val elementToChange = parent ?: moduleName // TODO: move logic out of UI - CheckboxRow( - moduleName, - checked = checked, - onCheckedChange = { - onCheckChange(elementToChange, it) - }, - textStyle = ts, - textModifier = Modifier.width(leftColumnMinSize) - ) - val text = when (moduleInfo) { - is CreateSdkDTO.ConfigurableModule -> { - val text = moduleInfo.existingVersion?.let { PySdkConfiguratorFrontendBundle.message("python.sdk.configurator.frontend.choose.modules.workspace.existing", it) } - ?: newText - val icon = icons[moduleInfo.createdByTool] - if (icon != null) { - Icon(icon, text) - } - text - } - is CreateSdkDTO.SameAs -> { - PySdkConfiguratorFrontendBundle.message("python.sdk.configurator.frontend.choose.modules.workspace.member", moduleInfo.parentModuleName) - } - } - Text(text, Modifier.fillMaxWidth()) + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = horizontalArrangement) { + Module(moduleName, moduleName in checked, onCheck, colPadding, moduleInfo, icons) } } } @@ -90,9 +57,46 @@ internal fun ModuleList( } @Composable -private fun measureText(text: String, textStyle: TextStyle): Dp { - val textMeasurer = rememberTextMeasurer() - return with(LocalDensity.current) { - textMeasurer.measure(text, textStyle).size.width.toDp() +private fun RowScope.Module( + moduleName: @NlsSafe String, + checked: Boolean, + onCheck: (ModuleName) -> Unit, + columnPadding: Dp, + moduleInfo: CreateSdkDTO, + icons: ImmutableMap, +) { + val colModifier = Modifier.weight(1f) + val newText = remember { PySdkConfiguratorFrontendBundle.message("python.sdk.configurator.frontend.choose.modules.new") } + val parent = when (moduleInfo) { + is CreateSdkDTO.ConfigurableModule -> null + is CreateSdkDTO.SameAs -> moduleInfo.parentModuleName } -} \ No newline at end of file + val elementToChange = parent ?: moduleName // TODO: move logic out of UI + CheckboxRow( + moduleName, + softWrap = false, + maxLines = 1, + checked = checked, + onCheckedChange = { + onCheck(elementToChange) + }, + modifier = colModifier + ) + Row(colModifier.padding(start = columnPadding)) { + val text = when (moduleInfo) { + is CreateSdkDTO.ConfigurableModule -> { + val text = moduleInfo.existingVersion?.let { PySdkConfiguratorFrontendBundle.message("python.sdk.configurator.frontend.choose.modules.workspace.existing", it) } + ?: newText + val icon = icons[moduleInfo.createdByTool] + if (icon != null) { + Icon(icon, text) + } + text + } + is CreateSdkDTO.SameAs -> { + PySdkConfiguratorFrontendBundle.message("python.sdk.configurator.frontend.choose.modules.workspace.member", moduleInfo.parentModuleName) + } + } + Text(text, Modifier.clickable(onClick = { onCheck(elementToChange) }), maxLines = 1, softWrap = false) + } +} diff --git a/python/python-sdk-configurator/frontend/src/frontendLib.kt b/python/python-sdk-configurator/frontend/src/frontendLib.kt index ba7635d2ade7..1e220f845f77 100644 --- a/python/python-sdk-configurator/frontend/src/frontendLib.kt +++ b/python/python-sdk-configurator/frontend/src/frontendLib.kt @@ -46,8 +46,9 @@ internal class ModulesViewModel(modulesDTO: ModulesDTO) { } } - fun clicked(what: ModuleName, checkBoxSet: Boolean) { + fun clicked(what: ModuleName) { val toChange = setOf(what) + children.getOrDefault(what, emptySet()) + val checkBoxSet = what !in checked if (checkBoxSet) { checked.addAll(toChange) }