mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 14:23:28 +07:00
PY-49929 Packages(fix): Allow selecting multiple packages simultaneously for a quick uninstall \ upgrade
GitOrigin-RevId: df8b02713955006061411b92cb89f40dc25046be
This commit is contained in:
committed by
intellij-monorepo-bot
parent
46871c21ac
commit
e66c52bd22
@@ -865,9 +865,10 @@ The Python plug-in provides smart editing for Python scripts. The feature set of
|
||||
<group id="PyPackageToolwindowContext">
|
||||
<action id="PyInstallPackage" class="com.jetbrains.python.packaging.toolwindow.actions.InstallPackageAction"/>
|
||||
<action id="PyInstallWithOptionPackage" class="com.jetbrains.python.packaging.toolwindow.actions.InstallWithOptionsPackageAction"/>
|
||||
<action id="PyDeletePackage" class="com.jetbrains.python.packaging.toolwindow.actions.DeletePackageAction"/>
|
||||
<action id="PyChangeVersionPackage" class="com.jetbrains.python.packaging.toolwindow.actions.ChangeVersionPackageAction"/>
|
||||
<action id="PyUpdateToLatestPackage" class="com.jetbrains.python.packaging.toolwindow.actions.UpdatePackageToLatestAction"/>
|
||||
<separator/>
|
||||
<action id="PyDeletePackage" class="com.jetbrains.python.packaging.toolwindow.actions.DeletePackageAction"/>
|
||||
</group>
|
||||
</actions>
|
||||
|
||||
|
||||
@@ -1529,7 +1529,7 @@ advertiser.code.cells.supported.by.pro=Code cells are supported by PyCharm Profe
|
||||
action.PyInstallPackage.text=Install
|
||||
action.PyDeletePackage.text=Uninstall
|
||||
action.PyUpdateToLatestPackage.text=Update to Latest
|
||||
action.PyChangeVersionPackage.text=Install Specific Version
|
||||
action.PyChangeVersionPackage.text=Change Version
|
||||
action.PyInstallWithOptionPackage.text=Install With Options
|
||||
python.toolwindow.packages.deleting.text=Uninstalling\u2026
|
||||
progress.text.installing=Installing\u2026
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging.repository
|
||||
|
||||
import com.jetbrains.python.PyBundle
|
||||
|
||||
class InstalledPyPackagedRepository : PyPackageRepository(PyBundle.message("python.toolwindow.packages.installed.label"), "", "") {}
|
||||
@@ -5,12 +5,12 @@ import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.JBColor
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.packaging.repository.InstalledPyPackagedRepository
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import com.jetbrains.python.packaging.toolwindow.model.*
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesTable
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesTableModel
|
||||
import com.jetbrains.python.packaging.toolwindow.packages.PyPackagingTableGroup
|
||||
import com.jetbrains.python.packaging.toolwindow.packages.table.PyPackagesTable
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagingTableGroup
|
||||
import java.awt.Rectangle
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
@@ -19,11 +19,12 @@ import javax.swing.JTable
|
||||
class PyPackagingTablesView(private val project: Project,
|
||||
private val container: JPanel,
|
||||
private val controller: PyPackagingToolWindowPanel) {
|
||||
private val repositories: MutableList<PyPackagingTableGroup<DisplayablePackage>> = mutableListOf()
|
||||
private val repositories: MutableList<PyPackagingTableGroup> = mutableListOf()
|
||||
private val installedPackages = PyPackagingTableGroup(
|
||||
object : PyPackageRepository(message("python.toolwindow.packages.installed.label"), "", "") {},
|
||||
PyPackagesTable(project, PyPackagesTableModel(), this, controller))
|
||||
InstalledPyPackagedRepository(),
|
||||
PyPackagesTable(project, this, controller))
|
||||
private val invalidRepositories: MutableMap<String, JPanel> = mutableMapOf()
|
||||
|
||||
init {
|
||||
installedPackages.addTo(container)
|
||||
installedPackages.expand()
|
||||
@@ -31,8 +32,10 @@ class PyPackagingTablesView(private val project: Project,
|
||||
|
||||
fun showSearchResult(installed: List<InstalledPackage>, repoData: List<PyPackagesViewData>) {
|
||||
updatePackages(installed, repoData)
|
||||
|
||||
installedPackages.expand()
|
||||
installedPackages.updateHeaderText(installed.size)
|
||||
|
||||
val tableToData = repositories.map { repo -> repo to repoData.find { it.repository.name == repo.name }!! }
|
||||
tableToData.forEach { (table, data) ->
|
||||
table.updateHeaderText(data.packages.size + data.moreItems)
|
||||
@@ -47,17 +50,21 @@ class PyPackagingTablesView(private val project: Project,
|
||||
|
||||
fun resetSearch(installed: List<InstalledPackage>, repoData: List<PyPackagesViewData>) {
|
||||
updatePackages(installed, repoData)
|
||||
|
||||
installedPackages.expand()
|
||||
installedPackages.updateHeaderText(null)
|
||||
|
||||
repositories.forEach {
|
||||
it.collapse()
|
||||
it.updateHeaderText(null)
|
||||
}
|
||||
|
||||
container.scrollRectToVisible(Rectangle(0, 0))
|
||||
}
|
||||
|
||||
private fun updatePackages(installed: List<InstalledPackage>, repoData: List<PyPackagesViewData>) {
|
||||
installedPackages.table.items = installed
|
||||
|
||||
val (validRepoData, invalid) = repoData.partition { it !is PyInvalidRepositoryViewData }
|
||||
|
||||
for (data in validRepoData) {
|
||||
@@ -73,7 +80,7 @@ class PyPackagingTablesView(private val project: Project,
|
||||
selectedItem?.let { existingRepo.table.selectPackage(it) }
|
||||
}
|
||||
else {
|
||||
val newTable = PyPackagesTable(project, PyPackagesTableModel(), this, controller)
|
||||
val newTable = PyPackagesTable(project, this, controller)
|
||||
newTable.items = withExpander
|
||||
|
||||
val newTableGroup = PyPackagingTableGroup(data.repository, newTable)
|
||||
@@ -129,7 +136,7 @@ class PyPackagingTablesView(private val project: Project,
|
||||
tableWithMatch.setRowSelectionInterval(exactMatch, exactMatch)
|
||||
}
|
||||
|
||||
fun requestSelection(table: JTable) {
|
||||
fun removeSelectionNotFormTable(table: JTable) {
|
||||
if (table != installedPackages.table) installedPackages.table.clearSelection()
|
||||
repositories.asSequence()
|
||||
.filter { it.table != table }
|
||||
@@ -178,4 +185,9 @@ class PyPackagingTablesView(private val project: Project,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectedPackages(): List<DisplayablePackage> {
|
||||
val repos = listOf(installedPackages) + repositories
|
||||
return repos.flatMap { it.table.selectedItems() }
|
||||
}
|
||||
}
|
||||
@@ -137,6 +137,7 @@ class PyPackagingToolWindowPanel(private val project: Project) : SimpleToolWindo
|
||||
|
||||
override fun uiDataSnapshot(sink: DataSink) {
|
||||
sink[PyPackagesUiComponents.SELECTED_PACKAGE_DATA_CONTEXT] = descriptionController.selectedPackage.get()
|
||||
sink[PyPackagesUiComponents.SELECTED_PACKAGES_DATA_CONTEXT] = this.packageListController.getSelectedPackages()
|
||||
super.uiDataSnapshot(sink)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstalledPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents.selectedPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents.selectedPackages
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -31,8 +32,7 @@ internal class ChangeVersionPackageAction : DumbAwareAction() {
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
val pkg = e.selectedPackage as? InstalledPackage
|
||||
e.presentation.isEnabledAndVisible = pkg != null
|
||||
e.presentation.isEnabledAndVisible = e.selectedPackage as? InstalledPackage != null && e.selectedPackages.size == 1
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
|
||||
|
||||
@@ -8,12 +8,23 @@ import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstalledPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents.selectedPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents.selectedPackages
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
internal class DeletePackageAction : DumbAwareAction() {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project ?: return
|
||||
val selectedPackages = e.selectedPackages.filterIsInstance<InstalledPackage>()
|
||||
if (selectedPackages.size > 1) {
|
||||
PyPackageCoroutine.launch(project, Dispatchers.IO) {
|
||||
selectedPackages.forEach { pkg ->
|
||||
project.service<PyPackagingToolWindowService>().deletePackage(pkg)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val pkg = e.selectedPackage as? InstalledPackage ?: return
|
||||
|
||||
val service = project.service<PyPackagingToolWindowService>()
|
||||
@@ -23,7 +34,7 @@ internal class DeletePackageAction : DumbAwareAction() {
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
e.presentation.isEnabledAndVisible = e.selectedPackage is InstalledPackage
|
||||
e.presentation.isEnabledAndVisible = e.selectedPackages.all { it is InstalledPackage }
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
|
||||
|
||||
@@ -3,12 +3,14 @@ package com.jetbrains.python.packaging.toolwindow.actions
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstallablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents.selectedPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents.selectedPackages
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -17,9 +19,20 @@ import java.awt.event.MouseEvent
|
||||
internal class InstallPackageAction : DumbAwareAction() {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project ?: return
|
||||
val selectedPackages = e.selectedPackages.filterIsInstance<InstallablePackage>()
|
||||
if (selectedPackages.size > 1) {
|
||||
PyPackageCoroutine.launch(project, Dispatchers.IO) {
|
||||
selectedPackages.forEach { pkg ->
|
||||
val specification = pkg.repository.createPackageSpecification(pkg.name, null)
|
||||
project.service<PyPackagingToolWindowService>().installPackage(specification)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
val pkg = e.selectedPackage as? InstallablePackage ?: return
|
||||
|
||||
|
||||
|
||||
PyPackageCoroutine.launch(project, Dispatchers.IO) {
|
||||
val service = PyPackagingToolWindowService.getInstance(project)
|
||||
val details = service.detailsForPackage(pkg)
|
||||
@@ -32,7 +45,7 @@ internal class InstallPackageAction : DumbAwareAction() {
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
e.presentation.isEnabledAndVisible = e.selectedPackage is InstallablePackage
|
||||
e.presentation.isEnabledAndVisible = e.selectedPackages.all { it is InstallablePackage }
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstallablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents.selectedPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents.selectedPackages
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -32,7 +33,7 @@ internal class InstallWithOptionsPackageAction : DumbAwareAction() {
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
e.presentation.isEnabledAndVisible = e.selectedPackage is InstallablePackage
|
||||
e.presentation.isEnabledAndVisible = e.selectedPackage as? InstallablePackage != null && e.selectedPackages.size == 1
|
||||
}
|
||||
|
||||
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.intellij.ui.ScrollPaneFactory
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingTablesView
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowPanel
|
||||
import com.jetbrains.python.packaging.toolwindow.model.DisplayablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstalledPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.PyPackagesViewData
|
||||
import javax.swing.BoxLayout
|
||||
@@ -37,4 +38,8 @@ class PyPackagesListController(val project: Project, val controller: PyPackaging
|
||||
fun selectPackage(name: String) {
|
||||
tablesView.selectPackage(name)
|
||||
}
|
||||
|
||||
fun getSelectedPackages(): List<DisplayablePackage> {
|
||||
return tablesView.getSelectedPackages()
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging.toolwindow.ui
|
||||
package com.jetbrains.python.packaging.toolwindow.packages
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import com.jetbrains.python.packaging.toolwindow.model.DisplayablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.packages.table.PyPackagesTable
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JPanel
|
||||
|
||||
internal class PyPackagingTableGroup<T : DisplayablePackage>(val repository: PyPackageRepository, val table: PyPackagesTable<T>) {
|
||||
internal class PyPackagingTableGroup(val repository: PyPackageRepository, val table: PyPackagesTable) {
|
||||
@NlsSafe
|
||||
val name: String = repository.name!!
|
||||
|
||||
@@ -19,7 +21,7 @@ internal class PyPackagingTableGroup<T : DisplayablePackage>(val repository: PyP
|
||||
private var itemsCount: Int? = null
|
||||
|
||||
|
||||
internal var items: List<T>
|
||||
internal var items: List<DisplayablePackage>
|
||||
get() = table.items
|
||||
set(value) {
|
||||
table.items = value
|
||||
@@ -47,11 +49,6 @@ internal class PyPackagingTableGroup<T : DisplayablePackage>(val repository: PyP
|
||||
panel.add(table)
|
||||
}
|
||||
|
||||
fun replace(row: Int, pkg: T) {
|
||||
table.removeRow(row)
|
||||
table.insertRow(row, pkg)
|
||||
}
|
||||
|
||||
fun removeFrom(panel: JPanel) {
|
||||
panel.remove(header)
|
||||
panel.remove(table)
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging.toolwindow.ui
|
||||
package com.jetbrains.python.packaging.toolwindow.packages
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.ui.hover.TableHoverListener
|
||||
@@ -10,6 +10,8 @@ import com.jetbrains.python.packaging.toolwindow.model.DisplayablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.ExpandResultNode
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstallablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstalledPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.packages.table.PyPackagesTable
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents
|
||||
import java.awt.Component
|
||||
import java.awt.font.TextAttribute
|
||||
import javax.swing.BoxLayout
|
||||
@@ -20,7 +22,9 @@ import javax.swing.table.DefaultTableCellRenderer
|
||||
|
||||
internal class PyPaginationAwareRenderer : DefaultTableCellRenderer() {
|
||||
private val nameLabel = JLabel().apply { border = JBUI.Borders.empty(0, 12) }
|
||||
|
||||
private val versionLabel = JLabel().apply { border = JBUI.Borders.emptyRight(12) }
|
||||
|
||||
private val linkLabel = JLabel(PyBundle.message("action.python.packages.install.text")).apply {
|
||||
border = JBUI.Borders.emptyRight(12)
|
||||
foreground = JBUI.CurrentTheme.Link.Foreground.ENABLED
|
||||
@@ -45,7 +49,7 @@ internal class PyPaginationAwareRenderer : DefaultTableCellRenderer() {
|
||||
row: Int,
|
||||
column: Int,
|
||||
): Component {
|
||||
val rowSelected = table.selectedRow == row
|
||||
val rowSelected = row in table.selectedRows
|
||||
val tableFocused = table.hasFocus()
|
||||
|
||||
if (value is ExpandResultNode) {
|
||||
@@ -101,7 +105,7 @@ internal class PyPaginationAwareRenderer : DefaultTableCellRenderer() {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun JLabel.updateUnderline(table: JTable, currentRow: Int) {
|
||||
val hoveredRow = TableHoverListener.getHoveredRow(table)
|
||||
val hoveredColumn = (table as PyPackagesTable<*>).hoveredColumn
|
||||
val hoveredColumn = (table as PyPackagesTable).hoveredColumn
|
||||
val underline = if (hoveredRow == currentRow && hoveredColumn == 1) TextAttribute.UNDERLINE_ON else -1
|
||||
|
||||
val attributes = font.attributes as MutableMap<TextAttribute, Any>
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging.toolwindow.packages.table
|
||||
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.ui.hover.TableHoverListener
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstallablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstalledPackage
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesUiComponents
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
|
||||
internal class PyPackageTableMouseAdapter(private val table: PyPackagesTable) : MouseAdapter() {
|
||||
val project: Project = table.project
|
||||
|
||||
val service
|
||||
get() = project.service<PyPackagingToolWindowService>()
|
||||
|
||||
override fun mouseClicked(e: MouseEvent) {
|
||||
if (e.clickCount != 1 || table.columnAtPoint(e.point) != 1) return // double click or click on package name column, nothing to be done
|
||||
val hoveredRow = TableHoverListener.getHoveredRow(table)
|
||||
val selectedPackage = table.items[hoveredRow]
|
||||
|
||||
if (selectedPackage is InstallablePackage) {
|
||||
PyPackageCoroutine.launch(project, Dispatchers.IO) {
|
||||
val details = service.detailsForPackage(selectedPackage)
|
||||
withContext(Dispatchers.Main) {
|
||||
PyPackagesUiComponents.createAvailableVersionsPopup(selectedPackage, details, project).show(RelativePoint(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (selectedPackage is InstalledPackage && selectedPackage.canBeUpdated) {
|
||||
PyPackageCoroutine.launch(project, Dispatchers.IO) {
|
||||
val nextVersion = selectedPackage.nextVersion ?: return@launch
|
||||
val specification = selectedPackage.repository.createPackageSpecification(selectedPackage.name, nextVersion.presentableText)
|
||||
project.service<PyPackagingToolWindowService>().updatePackage(specification)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging.toolwindow.packages.table
|
||||
|
||||
import com.intellij.ui.hover.TableHoverListener
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstallablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstalledPackage
|
||||
import java.awt.Cursor
|
||||
import javax.swing.JTable
|
||||
|
||||
internal class PyPackagesHoverListener(private val pyPackageTable: PyPackagesTable) : TableHoverListener() {
|
||||
|
||||
override fun onHover(table: JTable, row: Int, column: Int) {
|
||||
pyPackageTable.hoveredColumn = column
|
||||
if (column == 1) {
|
||||
table.repaint(table.getCellRect(row, column, true))
|
||||
val currentPackage = pyPackageTable.items[row]
|
||||
if (currentPackage is InstallablePackage
|
||||
|| (currentPackage is InstalledPackage && currentPackage.canBeUpdated)) {
|
||||
table.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
|
||||
return
|
||||
}
|
||||
}
|
||||
table.cursor = Cursor.getDefaultCursor()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging.toolwindow.ui
|
||||
package com.jetbrains.python.packaging.toolwindow.packages.table
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionGroup
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
@@ -8,58 +8,43 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.DoubleClickListener
|
||||
import com.intellij.ui.PopupHandler
|
||||
import com.intellij.ui.SideBorder
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.ui.hover.TableHoverListener
|
||||
import com.intellij.ui.table.JBTable
|
||||
import com.intellij.util.ui.ListTableModel
|
||||
import com.intellij.util.ui.NamedColorUtil
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingTablesView
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowPanel
|
||||
import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService
|
||||
import com.jetbrains.python.packaging.toolwindow.model.DisplayablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.ExpandResultNode
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstallablePackage
|
||||
import com.jetbrains.python.packaging.toolwindow.model.InstalledPackage
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.awt.Cursor
|
||||
import com.jetbrains.python.packaging.toolwindow.packages.PyPaginationAwareRenderer
|
||||
import com.jetbrains.python.packaging.toolwindow.ui.PyPackagesTableModel
|
||||
import java.awt.event.ActionEvent
|
||||
import java.awt.event.KeyEvent
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.AbstractAction
|
||||
import javax.swing.JTable
|
||||
import javax.swing.KeyStroke
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.table.TableCellRenderer
|
||||
|
||||
internal class PyPackagesTable<T : DisplayablePackage>(
|
||||
project: Project,
|
||||
model: ListTableModel<T>,
|
||||
internal class PyPackagesTable(
|
||||
val project: Project,
|
||||
tablesView: PyPackagingTablesView,
|
||||
val controller: PyPackagingToolWindowPanel,
|
||||
) : JBTable(model) {
|
||||
private val scope = PyPackageCoroutine.getIoScope(project)
|
||||
|
||||
private var lastSelectedElement: DisplayablePackage? = null
|
||||
) : JBTable(PyPackagesTableModel<DisplayablePackage>()) {
|
||||
internal var hoveredColumn = -1
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val listModel: ListTableModel<T>
|
||||
get() = model as ListTableModel<T>
|
||||
val model: PyPackagesTableModel<DisplayablePackage> = getModel() as PyPackagesTableModel<DisplayablePackage>
|
||||
|
||||
var items: List<T>
|
||||
get() = listModel.items
|
||||
var items: List<DisplayablePackage>
|
||||
get() = model.items
|
||||
set(value) {
|
||||
listModel.items = value.toMutableList()
|
||||
model.items = value.toMutableList()
|
||||
}
|
||||
|
||||
init {
|
||||
val service = project.service<PyPackagingToolWindowService>()
|
||||
setShowGrid(false)
|
||||
setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
|
||||
|
||||
val column = columnModel.getColumn(1)
|
||||
column.minWidth = 130
|
||||
column.maxWidth = 130
|
||||
@@ -69,51 +54,14 @@ internal class PyPackagesTable<T : DisplayablePackage>(
|
||||
|
||||
initCrossNavigation(service, tablesView)
|
||||
|
||||
val hoverListener = object : TableHoverListener() {
|
||||
override fun onHover(table: JTable, row: Int, column: Int) {
|
||||
hoveredColumn = column
|
||||
if (column == 1) {
|
||||
table.repaint(table.getCellRect(row, column, true))
|
||||
val currentPackage = items[row]
|
||||
if (currentPackage is InstallablePackage
|
||||
|| (currentPackage is InstalledPackage && currentPackage.canBeUpdated)) {
|
||||
cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
|
||||
return
|
||||
}
|
||||
}
|
||||
cursor = Cursor.getDefaultCursor()
|
||||
}
|
||||
}
|
||||
val hoverListener = PyPackagesHoverListener(this)
|
||||
hoverListener.addTo(this)
|
||||
|
||||
addMouseListener(object : MouseAdapter() {
|
||||
override fun mouseClicked(e: MouseEvent) {
|
||||
if (e.clickCount != 1 || columnAtPoint(e.point) != 1) return // double click or click on package name column, nothing to be done
|
||||
val hoveredRow = TableHoverListener.getHoveredRow(this@PyPackagesTable)
|
||||
val selectedPackage = this@PyPackagesTable.items[hoveredRow]
|
||||
|
||||
if (selectedPackage is InstallablePackage) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val details = service.detailsForPackage(selectedPackage)
|
||||
withContext(Dispatchers.Main) {
|
||||
PyPackagesUiComponents.createAvailableVersionsPopup(selectedPackage, details, project).show(RelativePoint(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (selectedPackage is InstalledPackage && selectedPackage.canBeUpdated) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val specification = selectedPackage.repository.createPackageSpecification(selectedPackage.name,
|
||||
selectedPackage.nextVersion!!.presentableText)
|
||||
project.service<PyPackagingToolWindowService>().updatePackage(specification)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
addMouseListener(PyPackageTableMouseAdapter(this))
|
||||
|
||||
selectionModel.addListSelectionListener {
|
||||
tablesView.requestSelection(this)
|
||||
val pkg = model.items.getOrNull(selectedRow)
|
||||
lastSelectedElement = pkg
|
||||
tablesView.removeSelectionNotFormTable(this)
|
||||
val pkg = selectedItem()
|
||||
if (pkg != null && pkg !is ExpandResultNode) {
|
||||
controller.packageSelected(pkg)
|
||||
}
|
||||
@@ -124,8 +72,9 @@ internal class PyPackagesTable<T : DisplayablePackage>(
|
||||
|
||||
object : DoubleClickListener() {
|
||||
override fun onDoubleClick(event: MouseEvent): Boolean {
|
||||
val pkg = model.items[selectedRow]
|
||||
if (pkg is ExpandResultNode) loadMoreItems(service, pkg)
|
||||
val pkg = selectedItem() ?: return true
|
||||
if (pkg is ExpandResultNode)
|
||||
loadMoreItems(service, pkg)
|
||||
return true
|
||||
}
|
||||
}.installOn(this)
|
||||
@@ -134,8 +83,20 @@ internal class PyPackagesTable<T : DisplayablePackage>(
|
||||
PopupHandler.installPopupMenu(this, packageActionGroup, "PackagePopup")
|
||||
}
|
||||
|
||||
override fun getCellRenderer(row: Int, column: Int) = PyPaginationAwareRenderer()
|
||||
|
||||
fun selectedItem(): T? = items.getOrNull(selectedRow)
|
||||
fun selectedItem(): DisplayablePackage? = items.getOrNull(selectedRow)
|
||||
|
||||
fun selectedItems(): Sequence<DisplayablePackage> {
|
||||
return selectedRows.asSequence().mapNotNull { items.getOrNull(it) }
|
||||
}
|
||||
|
||||
fun selectPackage(pkg: DisplayablePackage) {
|
||||
val index = items.indexOf(pkg)
|
||||
if (index != -1) {
|
||||
setRowSelectionInterval(index, index)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initCrossNavigation(service: PyPackagingToolWindowService, tablesView: PyPackagingTablesView) {
|
||||
getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ENTER_ACTION)
|
||||
@@ -181,43 +142,20 @@ internal class PyPackagesTable<T : DisplayablePackage>(
|
||||
})
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun loadMoreItems(service: PyPackagingToolWindowService, node: ExpandResultNode) {
|
||||
val result = service.getMoreResultsForRepo(node.repository, items.size - 1)
|
||||
items = items.dropLast(1) + (result.packages as List<T>)
|
||||
items = items.dropLast(1) + result.packages
|
||||
if (result.moreItems > 0) {
|
||||
node.more = result.moreItems
|
||||
items = items + listOf(node) as List<T>
|
||||
items = items + listOf(node)
|
||||
}
|
||||
this@PyPackagesTable.revalidate()
|
||||
this@PyPackagesTable.repaint()
|
||||
}
|
||||
|
||||
override fun getCellRenderer(row: Int, column: Int): TableCellRenderer {
|
||||
return PyPaginationAwareRenderer()
|
||||
}
|
||||
|
||||
fun selectPackage(pkg: DisplayablePackage) {
|
||||
val index = items.indexOf(pkg)
|
||||
if (index != -1) {
|
||||
setRowSelectionInterval(index, index)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearSelection() {
|
||||
lastSelectedElement = null
|
||||
super.clearSelection()
|
||||
}
|
||||
|
||||
internal fun removeRow(index: Int) = listModel.removeRow(index)
|
||||
internal fun insertRow(index: Int, pkg: T) = listModel.insertRow(index, pkg)
|
||||
|
||||
|
||||
companion object {
|
||||
private const val NEXT_ROW_ACTION = "selectNextRow"
|
||||
private const val PREVIOUS_ROW_ACTION = "selectPreviousRow"
|
||||
private const val ENTER_ACTION = "ENTER"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import javax.swing.*
|
||||
|
||||
object PyPackagesUiComponents {
|
||||
val SELECTED_PACKAGE_DATA_CONTEXT = DataKey.create<DisplayablePackage>("SELECTED_PACKAGE_DATA_CONTEXT")
|
||||
val SELECTED_PACKAGES_DATA_CONTEXT = DataKey.create<List<DisplayablePackage>>("SELECTED_PACKAGES_DATA_CONTEXT")
|
||||
|
||||
private val DataContext.selectedPackage: DisplayablePackage?
|
||||
get() = getData(SELECTED_PACKAGE_DATA_CONTEXT)
|
||||
@@ -36,6 +37,13 @@ object PyPackagesUiComponents {
|
||||
internal val AnActionEvent.selectedPackage: DisplayablePackage?
|
||||
get() = dataContext.selectedPackage
|
||||
|
||||
|
||||
private val DataContext.selectedPackages: List<DisplayablePackage>
|
||||
get() = getData(SELECTED_PACKAGES_DATA_CONTEXT) ?: emptyList()
|
||||
|
||||
internal val AnActionEvent.selectedPackages: List<DisplayablePackage>
|
||||
get() = dataContext.selectedPackages
|
||||
|
||||
fun createAvailableVersionsPopup(selectedPackage: DisplayablePackage, details: PythonPackageDetails, project: Project): ListPopup {
|
||||
return JBPopupFactory.getInstance().createListPopup(object : BaseListPopupStep<String>(null, details.availableVersions) {
|
||||
override fun onChosen(selectedValue: String?, finalChoice: Boolean): PopupStep<*>? {
|
||||
|
||||
Reference in New Issue
Block a user