diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/resources/promo/backgrounds/promotion_bg.svg b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/resources/promo/backgrounds/promotion_bg.svg
deleted file mode 100644
index ba211d578e33..000000000000
--- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/resources/promo/backgrounds/promotion_bg.svg
+++ /dev/null
@@ -1,103 +0,0 @@
-
diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedPromoService.kt b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedPromoService.kt
index 82c0bc0846d9..194d3b9bafa9 100644
--- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedPromoService.kt
+++ b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedPromoService.kt
@@ -9,6 +9,7 @@ import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.updateSettings.UpdateStrategyCustomization
import com.intellij.openapi.updateSettings.impl.*
import com.intellij.openapi.util.registry.Registry
+import com.intellij.pycharm.community.ide.impl.promo.WelcomeToUnifiedWelcomeScreenBanner
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex
@@ -48,6 +49,8 @@ internal class PyCommunityToUnifiedPromoService(val serviceScope: CoroutineScope
val propertiesComponent = PropertiesComponent.getInstance()
propertiesComponent.unsetValue(PROMO_UPDATE_DECLINED)
propertiesComponent.unsetValue(PROMO_REMIND_ME_LATER_LAST_TIME_CLICKED)
+ propertiesComponent.unsetValue(WelcomeToUnifiedWelcomeScreenBanner.BANNER_CLOSED_PROPERTY)
+
Registry.get(REG_PROMO_INTERVAL_SECONDS).resetToDefault()
}
diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedWelcomeScreenBanner.kt b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedWelcomeScreenBanner.kt
index 781bc1a6f0f1..5966df278b54 100644
--- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedWelcomeScreenBanner.kt
+++ b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyCommunityToUnifiedWelcomeScreenBanner.kt
@@ -4,18 +4,16 @@ package com.intellij.pycharm.community.ide.impl.promotion.communityToUnified
import com.intellij.ide.ui.laf.darcula.ui.DarculaButtonUI
import com.intellij.openapi.application.EDT
import com.intellij.openapi.components.service
-import com.intellij.openapi.wm.BannerStartPagePromoter
import com.intellij.openapi.wm.StartPagePromoter
+import com.intellij.pycharm.community.ide.impl.icons.PycharmCommunityIdeImplIcons
+import com.intellij.pycharm.community.ide.impl.promo.PyCommunityToUnifiedWelcomeScreenBannerBase
import com.intellij.pycharm.community.ide.impl.promotion.communityToUnified.statistics.PyCommunityUnifiedPromoFusCollector
-import com.intellij.pycharm.community.ide.impl.promotion.icons.PycharmCommunityIdeImplPromotionIcons
-import com.intellij.ui.components.panels.BackgroundRoundedPanel
import com.intellij.ui.components.panels.Wrapper
import com.intellij.ui.dsl.builder.EmptySpacingConfiguration
import com.intellij.ui.dsl.builder.TopGap
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.gridLayout.UnscaledGaps
import com.intellij.ui.dsl.gridLayout.UnscaledGapsY
-import com.intellij.util.IconUtil
import com.intellij.util.PlatformUtils
import com.intellij.util.ui.JBFont
import com.intellij.util.ui.JBUI
@@ -24,21 +22,16 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
-import java.awt.Graphics
-import java.awt.Graphics2D
-import java.awt.geom.RoundRectangle2D
-import javax.swing.BoxLayout
import javax.swing.Icon
import javax.swing.JComponent
import javax.swing.JPanel
-import kotlin.math.min
@ApiStatus.Internal
-internal class PyCommunityToUnifiedWelcomeScreenBanner : BannerStartPagePromoter() {
+internal class PyCommunityToUnifiedWelcomeScreenBanner : PyCommunityToUnifiedWelcomeScreenBannerBase() {
override val headerLabel: @Nls String = ""
override val actionLabel: @Nls String = ""
override val description: @Nls String = ""
- override val promoImage: Icon = PycharmCommunityIdeImplPromotionIcons.Backgrounds.Promotion_bg
+ override val promoImage: Icon = PycharmCommunityIdeImplIcons.Backgrounds.Promotion_bg
private val mainBannerPanel = createMainPanel()
@@ -58,19 +51,19 @@ internal class PyCommunityToUnifiedWelcomeScreenBanner : BannerStartPagePromoter
}
}
}
- return container
+ return wrapIntoSvgBackgroundPanel(container)
}
- override fun runAction() {}
-
override fun canCreatePromo(isEmptyState: Boolean): Boolean {
return PlatformUtils.isPyCharmCommunity()
}
+ override fun getMainPanel(): JPanel = container
+
override fun getPriorityLevel(): Int = StartPagePromoter.PRIORITY_LEVEL_HIGH
- private fun createMainPanel(): JPanel {
- val panel = panel {
+ private fun createMainPanel(): JPanel =
+ panel {
customizeSpacingConfiguration(EmptySpacingConfiguration()) {
row {
label(PyPromoSharedComponents.headerTitle)
@@ -101,46 +94,4 @@ internal class PyCommunityToUnifiedWelcomeScreenBanner : BannerStartPagePromoter
isOpaque = false
border = JBUI.Borders.empty(0, 12, 16, 0)
}
- return wrapIntoSvgBackgroundPanel(panel, JBUI.scale(16), promoImage)
- }
-
- private fun wrapIntoSvgBackgroundPanel(panel: JPanel, arc: Int, bgImage: Icon): JPanel {
- val svgPanel = SvgBackgroundRightAnchoredPanel(arc, bgImage).apply {
- layout = BoxLayout(this, BoxLayout.X_AXIS)
- }
- svgPanel.add(panel)
- return svgPanel
- }
-
- private class SvgBackgroundRightAnchoredPanel(
- private val arc: Int,
- private val bgImage: Icon,
- ) : BackgroundRoundedPanel(arc) {
-
- override fun paintComponent(g: Graphics) {
- super.paintComponent(g)
- if (width <= 0 || height <= 0) return
-
- val iw = bgImage.iconWidth
- val ih = bgImage.iconHeight
- if (iw <= 0 || ih <= 0) return
-
- val g2 = g.create() as Graphics2D
- try {
- g2.clip(RoundRectangle2D.Float(0f, 0f, width.toFloat(), height.toFloat(), arc.toFloat(), arc.toFloat()))
- val sx = width.toFloat() / iw
- val sy = height.toFloat() / ih
- val scale = min(sx, sy)
-
- val scaled = IconUtil.scale(bgImage, this, scale)
- // Right-anchored (no padding)
- val x = width - scaled.iconWidth
- val y = 0
- scaled.paintIcon(this, g2, x, y)
- }
- finally {
- g2.dispose()
- }
- }
- }
}
\ No newline at end of file
diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyPromoSharedComponents.kt b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyPromoSharedComponents.kt
index 156c2b63f08a..33406bf2a405 100644
--- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyPromoSharedComponents.kt
+++ b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/communityToUnified/PyPromoSharedComponents.kt
@@ -9,7 +9,7 @@ import java.awt.Color
internal object PyPromoSharedComponents {
- val popUpImg = PycharmCommunityIdeImplPromotionIcons.Images.Popup_img
+ val popUpImg = PycharmCommunityIdeImplPromotionIcons.Popup_img
val infoFontColor = JBColor(Color.decode("#6C707E"), Color.decode("#A8ADBD"))
// URLs used by promo-related messages
diff --git a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/icons/PycharmCommunityIdeImplPromotionIcons.java b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/icons/PycharmCommunityIdeImplPromotionIcons.java
index a4ad5d26b7fd..370c819993a6 100644
--- a/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/icons/PycharmCommunityIdeImplPromotionIcons.java
+++ b/python/ide/impl/intellij.pycharm.community.ide.impl.promotion/src/com/intellij/pycharm/community/ide/impl/promotion/icons/PycharmCommunityIdeImplPromotionIcons.java
@@ -15,12 +15,5 @@ public final class PycharmCommunityIdeImplPromotionIcons {
private static @NotNull Icon load(@NotNull String path, int cacheKey, int flags) {
return IconManager.getInstance().loadRasterizedIcon(path, PycharmCommunityIdeImplPromotionIcons.class.getClassLoader(), cacheKey, flags);
}
-
- public static final class Backgrounds {
- /** 360x128 */ public static final @NotNull Icon Promotion_bg = load("promo/backgrounds/promotion_bg.svg", -882473096, 0);
- }
-
- public static final class Images {
- /** 384x242 */ public static final @NotNull Icon Popup_img = load("promo/images/popup_img.svg", 986597622, 0);
- }
+ /** 384x242 */ public static final @NotNull Icon Popup_img = load("promo/images/popup_img.svg", 986597622, 0);
}
diff --git a/python/ide/impl/resources/backgrounds/promotion_bg.svg b/python/ide/impl/resources/backgrounds/promotion_bg.svg
new file mode 100644
index 000000000000..6795402cbc55
--- /dev/null
+++ b/python/ide/impl/resources/backgrounds/promotion_bg.svg
@@ -0,0 +1,113 @@
+
diff --git a/python/ide/impl/resources/icons/close.svg b/python/ide/impl/resources/icons/close.svg
new file mode 100644
index 000000000000..4000ed8344b0
--- /dev/null
+++ b/python/ide/impl/resources/icons/close.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/python/ide/impl/resources/icons/close_dark.svg b/python/ide/impl/resources/icons/close_dark.svg
new file mode 100644
index 000000000000..1ded8acf432f
--- /dev/null
+++ b/python/ide/impl/resources/icons/close_dark.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml b/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml
index bed3c3844f76..4b6a92a4be5f 100644
--- a/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml
+++ b/python/ide/impl/resources/intellij.pycharm.community.ide.impl.xml
@@ -139,6 +139,8 @@
+
@@ -163,6 +165,8 @@
+
diff --git a/python/ide/impl/resources/messages/PyCharmCommunityCustomizationBundle.properties b/python/ide/impl/resources/messages/PyCharmCommunityCustomizationBundle.properties
index 6cc92ba28ac3..378a29e9f4a2 100644
--- a/python/ide/impl/resources/messages/PyCharmCommunityCustomizationBundle.properties
+++ b/python/ide/impl/resources/messages/PyCharmCommunityCustomizationBundle.properties
@@ -77,11 +77,14 @@ promotion.update.to.unified.banner.header.title=Update to the unified PyCharm
promotion.update.to.unified.info.text=Starting with version 2025.3, all updates will be released exclusively for the unified PyCharm.
Update the IDE to keep receiving new features \u2013 all core functionalities will remain free.
promotion.update.to.unified.update.now.button.label=Update Now
promotion.update.to.unified.learn.more.hyper.link=Learn More
-promotion.update.to.unified.closed.banner.go.to.settings.info=You can find and install the new update in the Settings section
promotion.update.to.unified.skip.button.label=Skip
action.ShowPyCommunityToUnifiedTooltipAction.text=Community To Unified Tooltip
action.ShowPyCommunityToUnifiedTooltipAction.description=For testing purposes
action.ShowPyCommunityToUnifiedPromoDialogAction.text=Community To Unified Modal Promo
action.ShowPyCommunityToUnifiedPromoDialogAction.description=For testing purposes
action.ResetCommunityToUnifiedPromoStateAction.text=Reset Community To Unified Promo State
-action.ResetCommunityToUnifiedPromoStateAction.description=For testing purposes
\ No newline at end of file
+action.ResetCommunityToUnifiedPromoStateAction.description=For testing purposes
+promotion.welcome.to.unified.main.text=Everything is in one place! Switch between core and advanced
features within the same installation.
+promotion.welcome.to.unified.title=Welcome to unified PyCharm!
+promotion.welcome.to.unified.close=Close
+promotion.welcome.to.unified.close.banner=Close banner
\ No newline at end of file
diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/icons/PycharmCommunityIdeImplIcons.java b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/icons/PycharmCommunityIdeImplIcons.java
new file mode 100644
index 000000000000..08864ecbaf8e
--- /dev/null
+++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/icons/PycharmCommunityIdeImplIcons.java
@@ -0,0 +1,26 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.pycharm.community.ide.impl.icons;
+
+import com.intellij.ui.IconManager;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * NOTE THIS FILE IS AUTO-GENERATED
+ * DO NOT EDIT IT BY HAND, run "Generate icon classes" configuration instead
+ */
+@org.jetbrains.annotations.ApiStatus.Internal
+public final class PycharmCommunityIdeImplIcons {
+ private static @NotNull Icon load(@NotNull String path, int cacheKey, int flags) {
+ return IconManager.getInstance().loadRasterizedIcon(path, PycharmCommunityIdeImplIcons.class.getClassLoader(), cacheKey, flags);
+ }
+
+ public static final class Backgrounds {
+ /** 770x128 */ public static final @NotNull Icon Promotion_bg = load("backgrounds/promotion_bg.svg", -1173415960, 0);
+ }
+
+ public static final class Icons {
+ /** 16x16 */ public static final @NotNull Icon Close = load("icons/close.svg", -540869824, 2);
+ }
+}
diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/PyCommunityToUnifiedWelcomeScreenBannerBase.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/PyCommunityToUnifiedWelcomeScreenBannerBase.kt
new file mode 100644
index 000000000000..47ee28c8e8cf
--- /dev/null
+++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/PyCommunityToUnifiedWelcomeScreenBannerBase.kt
@@ -0,0 +1,93 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.pycharm.community.ide.impl.promo
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.observable.util.whenDisposed
+import com.intellij.openapi.util.Disposer
+import com.intellij.openapi.wm.BannerStartPagePromoter
+import com.intellij.openapi.wm.impl.IdeBackgroundUtil
+import com.intellij.util.SVGLoader
+import com.intellij.util.ui.GraphicsUtil
+import com.intellij.util.ui.JBInsets
+import com.intellij.util.ui.JBSwingUtilities
+import org.jetbrains.annotations.ApiStatus
+import org.jetbrains.annotations.Nls
+import java.awt.*
+import javax.swing.JComponent
+import javax.swing.JPanel
+
+@ApiStatus.Internal
+abstract class PyCommunityToUnifiedWelcomeScreenBannerBase : BannerStartPagePromoter() {
+ override val headerLabel: @Nls String = ""
+ override val actionLabel: @Nls String = ""
+ override val description: @Nls String = ""
+
+ abstract fun getMainPanel(): JPanel
+
+ override fun getPromotion(isEmptyState: Boolean): JComponent {
+ val mainPanel = getMainPanel()
+ return wrapIntoSvgBackgroundPanel(mainPanel)
+ }
+
+ override fun runAction() {}
+
+ fun wrapIntoSvgBackgroundPanel(panel: JPanel): JPanel {
+ val disposable = Disposer.newDisposable("BackgroundPanel")
+ val url = WelcomeToUnifiedWelcomeScreenBanner::class.java.getResource("/backgrounds/promotion_bg.svg") ?: return panel
+ val backgroundImage = SVGLoader.load(url, 1f)
+ val backgroundPanel = BackgroundPanel(BorderLayout(), backgroundImage, disposable)
+ backgroundPanel.add(panel, BorderLayout.CENTER)
+ backgroundPanel.isOpaque = false
+ return backgroundPanel
+ }
+
+ private class BackgroundPanel(layout: LayoutManager, private val image: Image, parentDisposable: Disposable) : JPanel(layout) {
+
+ private var disposable: Disposable? = null
+
+ init {
+ parentDisposable.whenDisposed { clearResources() }
+ installBackground()
+ }
+
+ override fun paintComponent(g: Graphics) {
+ super.paintComponent(g)
+ val g2 = g.create() as Graphics2D
+ try {
+ GraphicsUtil.setupAAPainting(g2)
+ g2.color = background
+ g2.fillRoundRect(0, 0, width, height, 16, 16)
+ }
+ finally {
+ g2.dispose()
+ }
+ }
+
+ override fun getComponentGraphics(g: Graphics?): Graphics? {
+ return JBSwingUtilities.runGlobalCGTransform(this, super.getComponentGraphics(g))
+ }
+
+ override fun updateUI() {
+ super.updateUI()
+ @Suppress("SENSELESS_COMPARISON")
+ if (image != null) {
+ installBackground()
+ }
+ }
+
+ private fun installBackground() {
+ clearResources()
+ disposable = Disposer.newDisposable("BackgroundPanel")
+ IdeBackgroundUtil.createTemporaryBackgroundTransform(this, image,
+ IdeBackgroundUtil.Fill.SCALE,
+ IdeBackgroundUtil.Anchor.MIDDLE_RIGHT,
+ 1f, JBInsets.emptyInsets(),
+ disposable)
+ }
+
+ private fun clearResources() {
+ disposable?.let { Disposer.dispose(it) }
+ disposable = null
+ }
+ }
+}
\ No newline at end of file
diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/WelcomeToUnifiedWelcomeScreenBanner.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/WelcomeToUnifiedWelcomeScreenBanner.kt
new file mode 100644
index 000000000000..457546a62987
--- /dev/null
+++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/promo/WelcomeToUnifiedWelcomeScreenBanner.kt
@@ -0,0 +1,112 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package com.intellij.pycharm.community.ide.impl.promo
+
+
+import com.intellij.ide.AppLifecycleListener
+import com.intellij.ide.util.PropertiesComponent
+import com.intellij.openapi.actionSystem.ActionUpdateThread
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.application.ApplicationInfo
+import com.intellij.openapi.project.DumbAwareAction
+import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBundle
+import com.intellij.pycharm.community.ide.impl.icons.PycharmCommunityIdeImplIcons
+import com.intellij.ui.JBColor
+import com.intellij.ui.components.panels.Wrapper
+import com.intellij.ui.dsl.builder.*
+import com.intellij.ui.dsl.gridLayout.UnscaledGaps
+import com.intellij.ui.dsl.gridLayout.UnscaledGapsY
+import com.intellij.util.IconUtil
+import com.intellij.util.PlatformUtils
+import com.intellij.util.ui.JBFont
+import com.intellij.util.ui.JBUI
+import java.awt.Color
+import java.awt.Dimension
+import javax.swing.Icon
+import javax.swing.JPanel
+
+private const val PREV_BUILD_WAS_PC_PROP = "pycharm.previous.version.was.community"
+
+class WelcomeToUnifiedWelcomeScreenBanner : PyCommunityToUnifiedWelcomeScreenBannerBase() {
+
+ companion object {
+ const val BANNER_CLOSED_PROPERTY: String = "WELCOME_TO_UNIFIED_BANNER_CLOSED"
+ }
+
+ private fun isBannerClosed(): Boolean = PropertiesComponent.getInstance().getBoolean(BANNER_CLOSED_PROPERTY)
+ private fun onBannerClosed() = PropertiesComponent.getInstance().setValue(BANNER_CLOSED_PROPERTY, true)
+
+ override val promoImage: Icon = PycharmCommunityIdeImplIcons.Backgrounds.Promotion_bg
+
+ private val infoFontColor = JBColor(Color.decode("#6C707E"), Color.decode("#A8ADBD"))
+ private val closeIcon = IconUtil.scale(PycharmCommunityIdeImplIcons.Icons.Close, null, 1.1f)
+
+ private val myCloseAction =
+ object : DumbAwareAction(PyCharmCommunityCustomizationBundle.message("promotion.welcome.to.unified.close"),
+ PyCharmCommunityCustomizationBundle.message("promotion.welcome.to.unified.close.banner"),
+ closeIcon) {
+ override fun getActionUpdateThread() = ActionUpdateThread.EDT
+ override fun actionPerformed(e: AnActionEvent) {
+ onBannerClosed()
+ container.isVisible = false
+ container.repaint()
+ }
+ }
+
+ val container: Wrapper = Wrapper(createMainPanel())
+
+ override fun canCreatePromo(isEmptyState: Boolean): Boolean {
+ val isUnified = ApplicationInfo.getInstance().build.productCode.equals("PY", ignoreCase = true)
+ return isUnified && !isBannerClosed() && isUpgradeFromCommunityToUnified()
+ }
+
+ private fun createMainPanel(): JPanel {
+ val panel = panel {
+ customizeSpacingConfiguration(EmptySpacingConfiguration()) {
+ row {
+ label(PyCharmCommunityCustomizationBundle.message("promotion.welcome.to.unified.title"))
+ .applyToComponent {
+ font = JBFont.h3().asBold()
+ }
+ actionButton(myCloseAction)
+ .applyToComponent {
+ setMinimumButtonSize { Dimension(closeIcon.iconWidth, closeIcon.iconHeight) }
+ }
+ .align(AlignX.RIGHT)
+ .align(AlignY.TOP)
+ .customize(UnscaledGaps(right = 8))
+ }.customize(customRowGaps = UnscaledGapsY(top = 12))
+ row {
+ text(PyCharmCommunityCustomizationBundle.message("promotion.welcome.to.unified.main.text"))
+ .applyToComponent {
+ foreground = infoFontColor
+ }
+ }.customize(customRowGaps = UnscaledGapsY(top = 4, bottom = 8))
+ }
+ row {
+ browserLink(PyCharmCommunityCustomizationBundle.message("promotion.update.to.unified.learn.more.hyper.link"),
+ "https://blog.jetbrains.com/pycharm/2025/04/unified-pycharm/")
+ }.topGap(TopGap.NONE)
+ }.apply {
+ isOpaque = false
+ border = JBUI.Borders.empty(0, 12, 16, 0)
+ }
+ return panel
+ }
+
+ override fun getMainPanel(): JPanel = container
+
+ private fun isUpgradeFromCommunityToUnified(): Boolean {
+ val currentCode = ApplicationInfo.getInstance().build.productCode
+
+ val prevWasCommunity = PropertiesComponent.getInstance().getBoolean(PREV_BUILD_WAS_PC_PROP, false)
+ return prevWasCommunity && currentCode.equals("PY", ignoreCase = true)
+ }
+}
+
+class CommunityToUnifiedListener : AppLifecycleListener {
+ override fun appStarted() {
+ if (PlatformUtils.isPyCharmCommunity()) {
+ PropertiesComponent.getInstance().setValue(PREV_BUILD_WAS_PC_PROP, true)
+ }
+ }
+}
\ No newline at end of file