From 0aa1646e4c38121f0d117e3a75d35166f5b0d9cf Mon Sep 17 00:00:00 2001 From: Pavel Porvatov Date: Mon, 27 Oct 2025 14:32:02 +0100 Subject: [PATCH] [ui] IJPL-203342 Islands: Navigation bar has wrong background color if placed on top or project gradient/toolbar is off GitOrigin-RevId: ff04cb364f9147247da7a125cb9a293da6afe76b --- .../frontend/src/NavBarRootPaneExtension.kt | 6 +-- .../impl/InternalUICustomization.kt | 8 ++- .../impl/islands/IslandsGradientPainter.kt | 12 ++++- .../impl/islands/IslandsUICustomization.kt | 52 +++++++++---------- .../header/CustomHeader.kt | 5 +- .../header/MacToolbarFrameHeader.kt | 5 +- .../wm/impl/headertoolbar/MainToolbar.kt | 5 +- .../com/intellij/toolWindow/ToolWindowPane.kt | 6 ++- .../intellij/toolWindow/ToolWindowToolbar.kt | 6 ++- .../intellij/ui/mac/WindowTabsComponent.java | 23 ++++++-- 10 files changed, 82 insertions(+), 46 deletions(-) diff --git a/platform/navbar/frontend/src/NavBarRootPaneExtension.kt b/platform/navbar/frontend/src/NavBarRootPaneExtension.kt index caf6124b2707..f3684b3b214d 100644 --- a/platform/navbar/frontend/src/NavBarRootPaneExtension.kt +++ b/platform/navbar/frontend/src/NavBarRootPaneExtension.kt @@ -13,8 +13,8 @@ import com.intellij.openapi.actionSystem.ex.ComboBoxAction import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.EDT import com.intellij.openapi.application.UI +import com.intellij.openapi.application.impl.BorderPainterHolder import com.intellij.openapi.application.impl.InternalUICustomization -import com.intellij.openapi.application.impl.TopNavBarComponentFacade import com.intellij.openapi.project.Project import com.intellij.openapi.wm.IdeRootPaneNorthExtension import com.intellij.openapi.wm.StatusBar @@ -277,7 +277,7 @@ internal open class MyNavBarWrapperPanel(private val project: Project, useAsComp } internal class MyTopNavBarWrapperPanel(project: Project, useAsComponent: Boolean) : - MyNavBarWrapperPanel(project, useAsComponent), TopNavBarComponentFacade { + MyNavBarWrapperPanel(project, useAsComponent), BorderPainterHolder { override var borderPainter: BorderPainter = DefaultBorderPainter() @@ -447,7 +447,7 @@ private object TopNavBarMode : NavBarMode { return withContext(Dispatchers.EDT) { setStatusBarCentralWidget(statusBar, null) val panel = MyTopNavBarWrapperPanel(project, useAsComponent = true) - InternalUICustomization.getInstance()?.configureTopNavBar(panel) + InternalUICustomization.getInstance()?.registerWindowBackgroundComponent(panel) panel } } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/InternalUICustomization.kt b/platform/platform-impl/src/com/intellij/openapi/application/impl/InternalUICustomization.kt index 3d19c66f4cd2..ca1d9841e3bb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/InternalUICustomization.kt +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/InternalUICustomization.kt @@ -95,7 +95,11 @@ open class InternalUICustomization { open fun configureMainToolbar(toolbar: MainToolbar) {} - open fun configureTopNavBar(navBar: TopNavBarComponentFacade) {} + /** + * For Islands theme: the components are painted with the IDE background or gradient if set. + * For other themes: has no effect + */ + open fun registerWindowBackgroundComponent(component: JComponent) {} open fun getEditorToolbarButtonLook(): ActionButtonLook? = null @@ -173,6 +177,6 @@ open class InternalUICustomization { } @ApiStatus.Internal -interface TopNavBarComponentFacade { +interface BorderPainterHolder { var borderPainter: BorderPainter } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsGradientPainter.kt b/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsGradientPainter.kt index bf830ae002b0..88b7ce1aa771 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsGradientPainter.kt +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsGradientPainter.kt @@ -25,12 +25,22 @@ import javax.swing.JComponent internal class IslandsGradientPainter(private val frame: IdeFrame, private val mainColor: Color, private val enabled: () -> Boolean) : AbstractPainter() { + /** + * The list of auto replaced colors. Should contain only very specific colors, don't add widely used like `Panel.background` + */ private val islandsGradientColors = setOf( + // Root components "MainWindow.background", "MainToolbar.background", "MainToolbar.inactiveBackground", "ToolWindow.Stripe.background", - "StatusBar.background") + "StatusBar.background", + + // Nav bar + "StatusBar.Breadcrumbs.hoverBackground", + "StatusBar.Breadcrumbs.selectionBackground", + "StatusBar.Breadcrumbs.selectionInactiveBackground" + ) private val projectWindowCustomizer = ProjectWindowCustomizerService.getInstance() diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsUICustomization.kt b/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsUICustomization.kt index d5ddfb790ea9..16ed17ff8c4a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsUICustomization.kt +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/islands/IslandsUICustomization.kt @@ -10,32 +10,28 @@ import com.intellij.ide.ui.experimental.ExperimentalUiCollector import com.intellij.openapi.actionSystem.DataSink import com.intellij.openapi.actionSystem.UiDataProvider import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.impl.BorderPainterHolder import com.intellij.openapi.application.impl.InternalUICustomization import com.intellij.openapi.application.impl.ToolWindowUIDecorator -import com.intellij.openapi.application.impl.TopNavBarComponentFacade import com.intellij.openapi.editor.impl.EditorHeaderComponent import com.intellij.openapi.editor.impl.SearchReplaceFacade import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.impl.EditorEmptyTextPainter import com.intellij.openapi.fileEditor.impl.EditorsSplitters -import com.intellij.openapi.ui.Divider -import com.intellij.openapi.ui.OnePixelDivider -import com.intellij.openapi.ui.Splittable +import com.intellij.openapi.ui.* +import com.intellij.openapi.util.Key import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.SystemInfoRt import com.intellij.openapi.wm.* import com.intellij.openapi.wm.ex.ToolWindowManagerEx import com.intellij.openapi.wm.impl.* import com.intellij.openapi.wm.impl.content.ContentLayout -import com.intellij.openapi.wm.impl.customFrameDecorations.header.CustomHeader import com.intellij.openapi.wm.impl.customFrameDecorations.header.CustomWindowHeaderUtil -import com.intellij.openapi.wm.impl.customFrameDecorations.header.MacToolbarFrameHeader import com.intellij.openapi.wm.impl.headertoolbar.MainToolbar import com.intellij.openapi.wm.impl.status.IdeStatusBarImpl import com.intellij.toolWindow.ToolWindowButtonManager import com.intellij.toolWindow.ToolWindowPane import com.intellij.toolWindow.ToolWindowPaneNewButtonManager -import com.intellij.toolWindow.ToolWindowToolbar import com.intellij.toolWindow.xNext.island.XNextIslandHolder import com.intellij.ui.* import com.intellij.ui.components.JBLayeredPane @@ -64,6 +60,10 @@ import javax.swing.SwingConstants import javax.swing.SwingUtilities import javax.swing.border.Border +private data class WindowBackgroundComponentData(val origOpaque: Boolean, val origBackground: Color?) + +private val WINDOW_BACKGROUND_COMPONENT_KEY: Key = Key.create("Islands.WINDOW_BACKGROUND_COMPONENT_KEY") + internal class IslandsUICustomization : InternalUICustomization() { private val isIslandsAvailable = ExperimentalUI.isNewUI() @@ -447,9 +447,11 @@ internal class IslandsUICustomization : InternalUICustomization() { } } - override fun configureTopNavBar(navBar: TopNavBarComponentFacade) { + override fun registerWindowBackgroundComponent(component: JComponent) { + component.putUserData(WINDOW_BACKGROUND_COMPONENT_KEY, WindowBackgroundComponentData(component.isOpaque, component.background)) + if (isManyIslandEnabled) { - configureMainFrameChildren(navBar as Component, true) + configureMainFrameChildren(component, true) } } @@ -464,29 +466,25 @@ internal class IslandsUICustomization : InternalUICustomization() { private fun configureMainFrameChildren(component: Component, install: Boolean) { when (component) { - is ToolWindowToolbar -> { - component.borderPainter = if (install) inactivePainter else DefaultBorderPainter() - } - is CustomHeader -> { - component.borderPainter = if (install) inactivePainter else DefaultBorderPainter() - } - is MacToolbarFrameHeader -> { - component.borderPainter = if (install) inactivePainter else DefaultBorderPainter() - } is IdeStatusBarImpl -> { component.borderPainter = if (install) inactivePainter else DefaultBorderPainter() } - is MainToolbar -> { + is BorderPainterHolder -> { component.borderPainter = if (install) inactivePainter else DefaultBorderPainter() } - is ToolWindowPane -> { - component.borderPainter = if (install) inactivePainter else DefaultBorderPainter() - } - is TopNavBarComponentFacade -> { - component.borderPainter = if (install) inactivePainter else DefaultBorderPainter() - } - is WindowTabsComponent -> { - component.borderPainter = if (install) inactivePainter else DefaultBorderPainter() + } + + if (component is JComponent) { + val data = component.getUserData(WINDOW_BACKGROUND_COMPONENT_KEY) + if (data != null) { + if (install) { + component.isOpaque = true + component.background = getMainBackgroundColor() + } + else { + component.isOpaque = data.origOpaque + component.background = data.origBackground + } } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/customFrameDecorations/header/CustomHeader.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/customFrameDecorations/header/CustomHeader.kt index 6107a6cd3741..020710c4577a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/customFrameDecorations/header/CustomHeader.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/customFrameDecorations/header/CustomHeader.kt @@ -6,6 +6,7 @@ import com.intellij.accessibility.AccessibilityUtils import com.intellij.icons.AllIcons import com.intellij.openapi.MnemonicHelper import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.impl.BorderPainterHolder import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.ui.JBPopupMenu import com.intellij.openapi.util.NlsActions @@ -48,7 +49,7 @@ internal fun updateWinControlsTheme(background: Color, customTitleBar: CustomTit customTitleBar.putProperty("controls.background.hovered", UIManager.getColor("TitlePane.Button.hoverBackground")) } -internal sealed class CustomHeader(@JvmField internal val window: Window) : JPanel() { +internal sealed class CustomHeader(@JvmField internal val window: Window) : JPanel(), BorderPainterHolder { companion object { val H: Int get() = 12 @@ -127,7 +128,7 @@ internal sealed class CustomHeader(@JvmField internal val window: Window) : JPan createProductIcon() } - internal var borderPainter: BorderPainter = DefaultBorderPainter() + override var borderPainter: BorderPainter = DefaultBorderPainter() init { isOpaque = true diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/customFrameDecorations/header/MacToolbarFrameHeader.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/customFrameDecorations/header/MacToolbarFrameHeader.kt index 30edacb90b8f..01a636c6477e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/customFrameDecorations/header/MacToolbarFrameHeader.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/customFrameDecorations/header/MacToolbarFrameHeader.kt @@ -11,6 +11,7 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.UI import com.intellij.openapi.application.asContextElement +import com.intellij.openapi.application.impl.BorderPainterHolder import com.intellij.openapi.application.impl.InternalUICustomization import com.intellij.openapi.wm.impl.ToolbarHolder import com.intellij.openapi.wm.impl.customFrameDecorations.header.titleLabel.SimpleCustomDecorationPath @@ -51,7 +52,7 @@ internal class MacToolbarFrameHeader( private val frame: JFrame, private val rootPane: JRootPane, private val isAlwaysCompact: Boolean = false, -) : JPanel(), MainFrameCustomHeader, ToolbarHolder, UISettingsListener { +) : JPanel(), MainFrameCustomHeader, ToolbarHolder, UISettingsListener, BorderPainterHolder { private var view: HeaderView private val updateRequests = MutableSharedFlow(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) @@ -72,7 +73,7 @@ internal class MacToolbarFrameHeader( val customTitleBar: WindowDecorations.CustomTitleBar? - internal var borderPainter: BorderPainter = DefaultBorderPainter() + override var borderPainter: BorderPainter = DefaultBorderPainter() init { // a colorful toolbar diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbar.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbar.kt index ac7b1c6f1135..e50f94db96b3 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbar.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/headertoolbar/MainToolbar.kt @@ -27,6 +27,7 @@ import com.intellij.openapi.actionSystem.toolbarLayout.ToolbarLayoutStrategy import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.EDT import com.intellij.openapi.application.UiWithModelAccess +import com.intellij.openapi.application.impl.BorderPainterHolder import com.intellij.openapi.application.impl.InternalUICustomization import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.keymap.impl.ui.ActionsTreeUtil @@ -112,12 +113,12 @@ class MainToolbar( isOpaque: Boolean = false, background: Color? = null, private val isFullScreen: () -> Boolean, -) : JPanel(HorizontalLayout(layoutGap)) { +) : JPanel(HorizontalLayout(layoutGap)), BorderPainterHolder { private val flavor: MainToolbarFlavor private val widthCalculationListeners = mutableSetOf() private val cachedWidths by lazy { ConcurrentHashMap() } - internal var borderPainter: BorderPainter = DefaultBorderPainter() + override var borderPainter: BorderPainter = DefaultBorderPainter() init { this.background = background diff --git a/platform/platform-impl/src/com/intellij/toolWindow/ToolWindowPane.kt b/platform/platform-impl/src/com/intellij/toolWindow/ToolWindowPane.kt index 7834e01a423a..eb1b6f3cc943 100644 --- a/platform/platform-impl/src/com/intellij/toolWindow/ToolWindowPane.kt +++ b/platform/platform-impl/src/com/intellij/toolWindow/ToolWindowPane.kt @@ -8,6 +8,7 @@ import com.intellij.ide.ui.LafManagerListener import com.intellij.ide.ui.UISettings import com.intellij.ide.ui.UISettingsListener import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.impl.BorderPainterHolder import com.intellij.openapi.application.impl.InternalUICustomization import com.intellij.openapi.diagnostic.debug import com.intellij.openapi.diagnostic.logger @@ -65,7 +66,7 @@ class ToolWindowPane private constructor( frame: JFrame, val paneId: String, @field:JvmField internal val buttonManager: ToolWindowButtonManager, -) : JLayeredPane(), UISettingsListener { +) : JLayeredPane(), UISettingsListener, BorderPainterHolder { companion object { const val TEMPORARY_ADDED: String = "TEMPORARY_ADDED" @@ -125,7 +126,8 @@ class ToolWindowPane private constructor( internal val frame: JFrame - internal var borderPainter: BorderPainter = DefaultBorderPainter() + override var borderPainter: BorderPainter = DefaultBorderPainter() + /** * This panel is the layered pane where all sliding tool windows are located. The DEFAULT * layer contains splitters. The PALETTE layer contains all sliding tool windows. diff --git a/platform/platform-impl/src/com/intellij/toolWindow/ToolWindowToolbar.kt b/platform/platform-impl/src/com/intellij/toolWindow/ToolWindowToolbar.kt index 0aa1c76cccc6..49db84324107 100644 --- a/platform/platform-impl/src/com/intellij/toolWindow/ToolWindowToolbar.kt +++ b/platform/platform-impl/src/com/intellij/toolWindow/ToolWindowToolbar.kt @@ -3,6 +3,7 @@ package com.intellij.toolWindow import com.intellij.accessibility.AccessibilityUtils +import com.intellij.openapi.application.impl.BorderPainterHolder import com.intellij.openapi.project.Project import com.intellij.openapi.ui.VerticalFlowLayout import com.intellij.openapi.util.NlsSafe @@ -29,7 +30,8 @@ import javax.swing.border.Border import kotlin.math.max @ApiStatus.Internal -abstract class ToolWindowToolbar(private val isPrimary: Boolean, val anchor: ToolWindowAnchor) : JBPanel() { +abstract class ToolWindowToolbar(private val isPrimary: Boolean, val anchor: ToolWindowAnchor) : JBPanel(), + BorderPainterHolder { lateinit var defaults: List internal abstract val bottomStripe: AbstractDroppableStripe @@ -42,7 +44,7 @@ abstract class ToolWindowToolbar(private val isPrimary: Boolean, val anchor: Too private var hasVisibleButtons = false private val visibleButtonsListeners = mutableListOf<() -> Unit>() - internal var borderPainter: BorderPainter = DefaultBorderPainter() + override var borderPainter: BorderPainter = DefaultBorderPainter() protected open fun init() { layout = myResizeManager.createLayout() diff --git a/platform/platform-impl/src/com/intellij/ui/mac/WindowTabsComponent.java b/platform/platform-impl/src/com/intellij/ui/mac/WindowTabsComponent.java index 83484364a1d7..cfc3aa0e41e4 100644 --- a/platform/platform-impl/src/com/intellij/ui/mac/WindowTabsComponent.java +++ b/platform/platform-impl/src/com/intellij/ui/mac/WindowTabsComponent.java @@ -6,6 +6,7 @@ import com.intellij.ide.IdeBundle; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.impl.BorderPainterHolder; import com.intellij.openapi.application.impl.InternalUICustomization; import com.intellij.openapi.components.ComponentManagerEx; import com.intellij.openapi.project.DumbAwareAction; @@ -39,7 +40,10 @@ import com.intellij.ui.tabs.UiDecorator; import com.intellij.ui.tabs.impl.*; import com.intellij.ui.tabs.impl.singleRow.WindowTabsLayout; import com.intellij.ui.tabs.impl.themes.DefaultTabTheme; -import com.intellij.util.ui.*; +import com.intellij.util.ui.GraphicsUtil; +import com.intellij.util.ui.JBFont; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.UIUtil; import kotlinx.coroutines.CoroutineScope; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -55,7 +59,7 @@ import java.beans.PropertyChangeListener; import java.util.*; @ApiStatus.Internal -public final class WindowTabsComponent extends JBTabsImpl { +public final class WindowTabsComponent extends JBTabsImpl implements BorderPainterHolder { private static final String TITLE_LISTENER_KEY = "TitleListener"; public static final String CLOSE_TAB_KEY = "CloseTab"; @@ -67,7 +71,7 @@ public final class WindowTabsComponent extends JBTabsImpl { private final Disposable myParentDisposable; private final Map myIndexes = new HashMap<>(); - public BorderPainter borderPainter = new DefaultBorderPainter(); + private BorderPainter borderPainter = new DefaultBorderPainter(); public WindowTabsComponent(@NotNull IdeFrameImpl nativeWindow, @Nullable Project project, @NotNull Disposable parentDisposable) { super(project, parentDisposable); @@ -94,6 +98,19 @@ public final class WindowTabsComponent extends JBTabsImpl { installDnD(); } + + @ApiStatus.Internal + @Override + public @NotNull BorderPainter getBorderPainter() { + return borderPainter; + } + + @ApiStatus.Internal + @Override + public void setBorderPainter(@NotNull BorderPainter painter) { + this.borderPainter = painter; + } + private static @NotNull Insets getContentInsets() { InternalUICustomization customization = InternalUICustomization.getInstance(); if (customization != null) {