diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties index 2db1e7ee5c03..2e8f647e8a7d 100644 --- a/python/pluginResources/messages/PyBundle.properties +++ b/python/pluginResources/messages/PyBundle.properties @@ -1277,6 +1277,9 @@ python.packaging.notification.installed=Package {0} installed python.packaging.notification.deleted=Package {0} deleted python.packaging.button.install.package=Install package python.packages.no.details.in.repo=

Could not read package description from {0}.

+python.packaging.toolwindow.advertisement=Try the redesigned packaging support in Python Packages toolwindow. +python.packaging.open.toolwindow.link=Go to toolwindow +python.packaging.interpreter.widget.manage.packages=Manage Packages\u2026 # Conda conda.packaging.install.progress=Installing conda package {0} diff --git a/python/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java b/python/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java index c4d4b2618df5..00dded62a011 100644 --- a/python/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java +++ b/python/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java @@ -17,12 +17,15 @@ package com.jetbrains.python.configuration; import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; +import com.intellij.ide.IdeBundle; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.UnnamedConfigurable; @@ -32,18 +35,25 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.DialogWrapperDialog; import com.intellij.openapi.ui.FixedSizeButton; +import com.intellij.openapi.ui.popup.IconButton; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.registry.Registry; -import com.intellij.ui.CollectionComboBoxModel; -import com.intellij.ui.ComboboxSpeedSearch; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.*; import com.intellij.ui.components.DropDownLink; +import com.intellij.ui.components.panels.HorizontalLayout; +import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.util.NullableConsumer; import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.components.BorderLayoutPanel; import com.intellij.webcore.packaging.PackagesNotificationPanel; import com.jetbrains.python.PyBundle; import com.jetbrains.python.packaging.PyPackageManagers; @@ -55,6 +65,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; import java.awt.*; import java.awt.event.ItemEvent; import java.util.ArrayList; @@ -62,6 +74,7 @@ import java.util.List; import java.util.Map; import java.util.function.Consumer; +import static com.intellij.codeInsight.hint.HintUtil.PROMOTION_PANE_KEY; import static com.jetbrains.python.sdk.PySdkRenderingKt.groupModuleSdksByTypes; public class PyActiveSdkConfigurable implements UnnamedConfigurable { @@ -226,7 +239,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { c.gridy++; c.gridwidth = 3; c.weightx = 0.0; - result.add(new JLabel(" "), c); + result.add(buildToolWindowAdvertisement(project), c); c.gridx = 0; c.gridy++; @@ -294,6 +307,44 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { ); } + private static JComponent buildToolWindowAdvertisement(Project project) { + var promotionKey = "PY_PACKAGES_AD_HIDDEN"; + if (PropertiesComponent.getInstance().isValueSet(promotionKey)) return new JLabel(" "); + var panel = new BorderLayoutPanel(); + panel.setBorder(JBUI.Borders.empty(7)); + panel.setAlignmentX(Component.LEFT_ALIGNMENT); + JLabel label = new JLabel(PyBundle.message("python.packaging.toolwindow.advertisement")); + label.setIcon(AllIcons.Ide.Gift); + panel.addToCenter(label); + panel.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getColor(PROMOTION_PANE_KEY)); + panel.setMinimumSize(new Dimension(400, panel.getMinimumSize().height)); + + var linkPanel = new NonOpaquePanel(new HorizontalLayout(12)); + HyperlinkLabel link = new HyperlinkLabel(PyBundle.message("python.packaging.open.toolwindow.link")); + link.addHyperlinkListener(new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent e) { + Window window = ComponentUtil.getActiveWindow(); + if (window instanceof DialogWrapperDialog dialog) { + dialog.getDialogWrapper().close(DialogWrapper.CANCEL_EXIT_CODE); + ToolWindow toolWindow = ToolWindowManager.getInstance(project).getToolWindow("Python Packages"); + if (toolWindow != null) toolWindow.show(); + } + } + }); + linkPanel.add(link); + var closeAction = new InplaceButton( + new IconButton(IdeBundle.message("do.not.ask.me.again"), AllIcons.Actions.Close, AllIcons.Actions.CloseHovered), + e -> { + PropertiesComponent.getInstance().setValue(promotionKey, true); + panel.removeAll(); + panel.setBackground(JBColor.background()); + }); + linkPanel.add(closeAction); + panel.addToRight(linkPanel); + return panel; + } + /** * @param selectedSdk the selected Python SDK before closing "Python Interpreters" dialog if the user clicked "OK" and {@code null} if the * user clicked "Cancel" button diff --git a/python/src/com/jetbrains/python/sdk/PySdkPopupFactory.kt b/python/src/com/jetbrains/python/sdk/PySdkPopupFactory.kt index e9d029ed7af7..90da60c25921 100644 --- a/python/src/com/jetbrains/python/sdk/PySdkPopupFactory.kt +++ b/python/src/com/jetbrains/python/sdk/PySdkPopupFactory.kt @@ -2,6 +2,7 @@ package com.jetbrains.python.sdk import com.intellij.ide.DataManager +import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DefaultActionGroup @@ -14,6 +15,7 @@ import com.intellij.openapi.ui.popup.ListPopup import com.intellij.openapi.util.Condition import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.util.registry.Registry +import com.intellij.openapi.wm.ToolWindowManager import com.intellij.util.text.trimMiddle import com.intellij.util.ui.SwingHelper import com.jetbrains.python.PyBundle @@ -92,6 +94,11 @@ class PySdkPopupFactory(val project: Project, val module: Module) { if (!Registry.get("python.use.targets.api").asBoolean()) { group.add(AddInterpreterAction(project, module, currentSdk)) } + group.add(object : AnAction(PyBundle.message("python.packaging.interpreter.widget.manage.packages")) { + override fun actionPerformed(e: AnActionEvent) { + ToolWindowManager.getInstance(project).getToolWindow("Python Packages")?.show() + } + }) return JBPopupFactory.getInstance().createActionGroupPopup( PyBundle.message("configurable.PyActiveSdkModuleConfigurable.python.interpreter.display.name"),