mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[python] PY-79486: (WIP): Fix "search module" field, add "select all" checkbox.
UI arranged with the sketch GitOrigin-RevId: cf0167b06757767f271ae4d33572ef2a42e16f41
This commit is contained in:
committed by
intellij-monorepo-bot
parent
01ccd0b87f
commit
2ed2f281a4
+3
-2
@@ -1,7 +1,8 @@
|
||||
python.sdk.configurator.frontend.choose.modules.title=Configure Modules Environment
|
||||
python.sdk.configurator.frontend.choose.modules.text=Pick the ones you want to use to run code and set up environment for them
|
||||
python.sdk.configurator.frontend.choose.modules.project.structure=Modules:
|
||||
python.sdk.configurator.frontend.choose.modules.environment=Environments
|
||||
python.sdk.configurator.frontend.choose.modules.project.search=Search
|
||||
python.sdk.configurator.frontend.choose.modules.select.all=Select All
|
||||
python.sdk.configurator.frontend.choose.modules.environment=Environment
|
||||
python.sdk.configurator.frontend.choose.modules.workspace.existing=Use existing version {0}
|
||||
python.sdk.configurator.frontend.choose.modules.new=Create new environment
|
||||
python.sdk.configurator.frontend.choose.modules.configure=Configure
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.jewel.ui.component.*
|
||||
import org.jetbrains.jewel.ui.component.styling.LocalCheckboxStyle
|
||||
import org.jetbrains.jewel.ui.icon.IconKey
|
||||
import org.jetbrains.jewel.ui.icon.IntelliJIconKey
|
||||
import org.jetbrains.jewel.ui.icons.AllIconsKeys
|
||||
|
||||
/**
|
||||
* List of modules from [viewModel]. Screen sizes are in physical pixels
|
||||
@@ -36,36 +37,40 @@ internal fun ModuleList(
|
||||
viewModel.processFilterUpdates()
|
||||
}
|
||||
val topLabel = remember { message("python.sdk.configurator.frontend.choose.modules.text") }
|
||||
val projectStructureLabel = remember { message("python.sdk.configurator.frontend.choose.modules.project.structure") }
|
||||
val searchTitle = remember { message("python.sdk.configurator.frontend.choose.modules.project.search") }
|
||||
val environmentLabel = remember { message("python.sdk.configurator.frontend.choose.modules.environment") }
|
||||
val selectAllLabel = remember { message("python.sdk.configurator.frontend.choose.modules.select.all") }
|
||||
|
||||
val border = Modifier.border(Stroke.Alignment.Outside, 1.dp, JewelTheme.globalColors.borders.normal)
|
||||
val space = 5.dp
|
||||
VerticallyScrollableContainer(Modifier.padding(space).then(border).fillMaxSize()) {
|
||||
val checkboxArrangement = Arrangement.spacedBy(space)
|
||||
Column(Modifier.fillMaxSize(), verticalArrangement = checkboxArrangement) {
|
||||
val checkBoxArrangement = Arrangement.spacedBy(space)
|
||||
Column(Modifier.fillMaxSize(), verticalArrangement = checkBoxArrangement) {
|
||||
Text(text = topLabel, Modifier.padding(space))
|
||||
|
||||
Column(Modifier.fillMaxSize(), verticalArrangement = checkboxArrangement) {
|
||||
Row(Modifier.fillMaxSize().then(border).padding(space), horizontalArrangement = checkboxArrangement) {
|
||||
ModuleRow(
|
||||
left = { modifier ->
|
||||
Row(modifier) {
|
||||
Text(projectStructureLabel)
|
||||
Spacer(Modifier.width(1.dp))
|
||||
TextField(viewModel.moduleFilter, undecorated = true, modifier = Modifier.weight(1f))
|
||||
}
|
||||
},
|
||||
right = { modifier ->
|
||||
Text(environmentLabel, modifier = modifier)
|
||||
},
|
||||
)
|
||||
}
|
||||
for (module in viewModel.filteredModules) {
|
||||
key(module.name) {
|
||||
Row(verticalAlignment = Alignment.Top, horizontalArrangement = checkboxArrangement, modifier = Modifier.padding(horizontal = space)) {
|
||||
Module(module.name in viewModel.checkedModules, viewModel::clicked, module, viewModel.icons, checkboxArrangement)
|
||||
|
||||
Row(Modifier.fillMaxSize().then(border).padding(space), horizontalArrangement = checkBoxArrangement, verticalAlignment = Alignment.CenterVertically) {
|
||||
ModuleRow(
|
||||
left = { modifier ->
|
||||
Row(modifier) {
|
||||
TextField(viewModel.moduleFilter, undecorated = false, modifier = Modifier.weight(1f), leadingIcon = { Icon(AllIconsKeys.Actions.Find, searchTitle) })
|
||||
}
|
||||
},
|
||||
right = { modifier ->
|
||||
Text(environmentLabel, modifier = modifier)
|
||||
},
|
||||
)
|
||||
}
|
||||
Column(Modifier.fillMaxSize(), verticalArrangement = checkBoxArrangement) {
|
||||
Row(verticalAlignment = Alignment.Top) {
|
||||
// TODO: Get real size instead of "invisible" checkbox
|
||||
OpenArrow(false, {}, Modifier.alpha(0.0f))
|
||||
TriStateCheckboxRow(selectAllLabel, viewModel.selectAllState, viewModel::selectAllClicked)
|
||||
Spacer(Modifier.weight(1f))
|
||||
}
|
||||
for (module in viewModel.filteredParentModules) {
|
||||
key(module.name) {
|
||||
Module(module.name in viewModel.checkedModules, viewModel::moduleClicked, module, viewModel.icons, checkBoxArrangement)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +98,7 @@ private fun Module(
|
||||
Row(verticalAlignment = Alignment.Top, horizontalArrangement = checkBoxArrangement) {
|
||||
ModuleRow(
|
||||
left = { modifier ->
|
||||
Row(modifier) {
|
||||
Row(modifier, verticalAlignment = if (subModuleOpened) Alignment.Top else Alignment.CenterVertically) {
|
||||
// TODO: Get real size instead of "invisible" checkbox
|
||||
OpenArrow(subModuleOpened, { subModuleOpened = !subModuleOpened },
|
||||
modifier = if (module.childModules.isEmpty()) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.intellij.python.sdkConfigurator.frontend
|
||||
import androidx.compose.foundation.text.input.TextFieldState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateSet
|
||||
import androidx.compose.ui.state.ToggleableState
|
||||
import com.intellij.python.common.tools.ToolId
|
||||
import com.intellij.python.common.tools.getIcon
|
||||
import com.intellij.python.sdkConfigurator.common.impl.ModuleDTO
|
||||
@@ -21,7 +22,7 @@ import org.jetbrains.jewel.ui.icon.IntelliJIconKey
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
/**
|
||||
* UI should display [filteredModules] (either enabled or disabled). On each click call [clicked].
|
||||
* UI should display [filteredParentModules] (either enabled or disabled). On each click call [moduleClicked].
|
||||
* Show filter from [moduleFilter]
|
||||
* Result can be taken from [checkedModules].
|
||||
* While composable is displayed. call [processFilterUpdates]
|
||||
@@ -41,33 +42,55 @@ internal class ModulesViewModel(modulesDTO: ModulesDTO) {
|
||||
Pair(toolId, icon)
|
||||
}.toTypedArray())
|
||||
|
||||
private val modules: List<ModuleDTO> = modulesDTO.modules.sortedBy { it.name }
|
||||
private val parentModules: List<ModuleDTO> = modulesDTO.modules.sortedBy { it.name }
|
||||
private val parentModuleNames: Set<ModuleName> = parentModules.map { it.name }.toSet()
|
||||
|
||||
var filteredModules: List<ModuleDTO> by mutableStateOf(modules)
|
||||
var selectAllState: ToggleableState by mutableStateOf(ToggleableState.Off)
|
||||
var filteredParentModules: List<ModuleDTO> by mutableStateOf(parentModules)
|
||||
val checkedModules: SnapshotStateSet<ModuleName> = mutableStateSetOf()
|
||||
val moduleFilter = TextFieldState()
|
||||
|
||||
// parent -> children
|
||||
private val children: ImmutableMap<ModuleName, ImmutableSet<ModuleName>> = filteredModules.associate {
|
||||
private val children: ImmutableMap<ModuleName, ImmutableSet<ModuleName>> = filteredParentModules.associate {
|
||||
Pair(it.name, it.childModules.toPersistentSet())
|
||||
}.toPersistentMap()
|
||||
|
||||
private val parentOnlyCheckedModules: Set<ModuleName> get() = parentModuleNames.intersect(checkedModules)
|
||||
|
||||
// child -> parent
|
||||
private val parents: ImmutableMap<ModuleName, ModuleName> = children.flatMap { (parent, children) ->
|
||||
children.map { Pair(it, parent) }
|
||||
}.toMap().toImmutableMap()
|
||||
|
||||
fun clicked(module: ModuleName) {
|
||||
val what = parents[module] ?: module // Get parent if child module
|
||||
val toChange = setOf(what) + children.getOrDefault(what, emptySet()) // Get children if parent
|
||||
val checkBoxSet = what !in checkedModules
|
||||
if (checkBoxSet) {
|
||||
checkedModules.addAll(toChange)
|
||||
fun selectAllClicked() {
|
||||
val checked = when (selectAllState) {
|
||||
ToggleableState.On -> false
|
||||
ToggleableState.Off, ToggleableState.Indeterminate -> true
|
||||
}
|
||||
setParentModules(checked = checked, parentModulesToSet = parentModuleNames.toTypedArray())
|
||||
}
|
||||
|
||||
fun moduleClicked(module: ModuleName) {
|
||||
val parentModule = parents[module] ?: module // Get parent if child module
|
||||
val alreadyChecked = parentModule in checkedModules
|
||||
setParentModules(checked = !alreadyChecked, parentModule)
|
||||
}
|
||||
|
||||
private fun setParentModules(checked: Boolean, vararg parentModulesToSet: ModuleName) {
|
||||
val parentModulesToSet = parentModulesToSet.toSet()
|
||||
val parentsAndChildrenModules = parentModulesToSet + parentModulesToSet.flatMap { children.getOrDefault(it, emptySet()) } // Get children if parent
|
||||
if (checked) {
|
||||
checkedModules.addAll(parentsAndChildrenModules)
|
||||
}
|
||||
else {
|
||||
checkedModules.removeAll(toChange)
|
||||
checkedModules.removeAll(parentsAndChildrenModules)
|
||||
}
|
||||
callEnabledButtonListener()
|
||||
selectAllState = when (parentOnlyCheckedModules.size) {
|
||||
0 -> ToggleableState.Off
|
||||
parentModules.size -> ToggleableState.On // All parent modules are checked
|
||||
else -> ToggleableState.Indeterminate
|
||||
}
|
||||
}
|
||||
|
||||
private fun callEnabledButtonListener() {
|
||||
@@ -83,7 +106,7 @@ internal class ModulesViewModel(modulesDTO: ModulesDTO) {
|
||||
.collectLatest { filter ->
|
||||
withContext(Dispatchers.Default) {
|
||||
val filter = filter.trim()
|
||||
filteredModules = if (filter.isEmpty()) modules else modules.filter { filter in it.name }
|
||||
filteredParentModules = if (filter.isEmpty()) parentModules else parentModules.filter { filter in it.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user