diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalToolWindowManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalToolWindowManager.java index 0df78712192d..71a79ac9b7de 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalToolWindowManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ui/ExternalToolWindowManager.java @@ -14,6 +14,7 @@ import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.impl.ToolWindowHeadlessManagerImpl; +import com.intellij.openapi.wm.impl.ToolWindowImpl; import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -30,7 +31,8 @@ import java.util.Set; * * @author Denis Zhdanov */ -public final class ExternalToolWindowManager { +public class ExternalToolWindowManager { + @SuppressWarnings("unchecked") public static void handle(@NotNull Project project) { for (final ExternalSystemManager manager : ExternalSystemApiUtil.getAllManagers()) { @@ -80,7 +82,14 @@ public final class ExternalToolWindowManager { @Nullable public static ToolWindow getToolWindow(@NotNull Project project, @NotNull ProjectSystemId externalSystemId) { - ToolWindow result = ToolWindowManager.getInstance(project).getToolWindow(externalSystemId.getReadableName()); + final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); + if (toolWindowManager == null) { + return null; + } + ToolWindow result = toolWindowManager.getToolWindow(externalSystemId.getReadableName()); + if (result instanceof ToolWindowImpl) { + ((ToolWindowImpl)result).ensureContentInitialized(); + } if (result == null && ApplicationManager.getApplication().isUnitTestMode()) { result = new ToolWindowHeadlessManagerImpl.MockToolWindow(project); } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java index 5a3bbc3b47da..3ffc227167e2 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java @@ -86,6 +86,7 @@ import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.ex.ProgressIndicatorEx; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; +import com.intellij.openapi.wm.impl.ToolWindowImpl; import com.intellij.pom.Navigatable; import com.intellij.pom.NonNavigatable; import com.intellij.util.ArrayUtil; @@ -106,7 +107,10 @@ import java.util.function.Supplier; import static com.intellij.openapi.externalSystem.settings.AbstractExternalSystemLocalSettings.SyncType.*; import static com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.doWriteAction; -public final class ExternalSystemUtil { +/** + * @author Denis Zhdanov + */ +public class ExternalSystemUtil { private static final Logger LOG = Logger.getInstance(ExternalSystemUtil.class); @NotNull private static final Map RUNNER_IDS = new HashMap<>(); @@ -181,7 +185,16 @@ public final class ExternalSystemUtil { @Nullable public static ToolWindow ensureToolWindowContentInitialized(@NotNull Project project, @NotNull ProjectSystemId externalSystemId) { - return ToolWindowManager.getInstance(project).getToolWindow(externalSystemId.getReadableName()); + final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); + if (toolWindowManager == null) return null; + + final ToolWindow toolWindow = toolWindowManager.getToolWindow(externalSystemId.getReadableName()); + if (toolWindow == null) return null; + + if (toolWindow instanceof ToolWindowImpl) { + ((ToolWindowImpl)toolWindow).ensureContentInitialized(); + } + return toolWindow; } /** @@ -269,9 +282,9 @@ public final class ExternalSystemUtil { } } - @NotNull + @Nullable private static String extractDetails(@NotNull Throwable e) { - Throwable unwrapped = RemoteUtil.unwrap(e); + final Throwable unwrapped = RemoteUtil.unwrap(e); if (unwrapped instanceof ExternalSystemException) { return ((ExternalSystemException)unwrapped).getOriginalReason(); } diff --git a/platform/platform-api/src/com/intellij/openapi/MnemonicHelper.java b/platform/platform-api/src/com/intellij/openapi/MnemonicHelper.java index fd1f50570b2b..1bf412954cef 100644 --- a/platform/platform-api/src/com/intellij/openapi/MnemonicHelper.java +++ b/platform/platform-api/src/com/intellij/openapi/MnemonicHelper.java @@ -35,7 +35,7 @@ import java.util.function.IntPredicate; * * @author lesya */ -public final class MnemonicHelper extends ComponentTreeWatcher { +public class MnemonicHelper extends ComponentTreeWatcher { private static final Logger LOG = Logger.getInstance(MnemonicHelper.class); public static final Key MNEMONIC_CHECKER = Key.create("MNEMONIC_CHECKER"); @@ -190,16 +190,11 @@ public final class MnemonicHelper extends ComponentTreeWatcher { private static final MnemonicFixer ourMnemonicFixer = new MnemonicFixer(); - private static final class MnemonicFixer implements ContainerListener { + private static class MnemonicFixer implements ContainerListener { void addTo(Component component) { for (Component c : UIUtil.uiTraverser(component)) { - if (c instanceof Container) { - ((Container)c).addContainerListener(this); - } - if (c instanceof ActionButtonComponent) { - assert c instanceof JComponent; - fixMacMnemonicKeyStroke((JComponent)c, null); - } + if (c instanceof Container) ((Container)c).addContainerListener(this); + if (c instanceof ActionButtonComponent) fixMacMnemonicKeyStroke((JComponent)c, null); MnemonicWrapper.getWrapper(c); } } diff --git a/platform/platform-api/src/com/intellij/openapi/project/DumbUnawareHider.java b/platform/platform-api/src/com/intellij/openapi/project/DumbUnawareHider.java index e58723ed9c3e..10aad38f0b95 100644 --- a/platform/platform-api/src/com/intellij/openapi/project/DumbUnawareHider.java +++ b/platform/platform-api/src/com/intellij/openapi/project/DumbUnawareHider.java @@ -1,4 +1,19 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.intellij.openapi.project; import com.intellij.ui.components.JBPanelWithEmptyText; @@ -10,7 +25,8 @@ import java.awt.*; /** * @author peter */ -public final class DumbUnawareHider extends JBPanelWithEmptyText { +public class DumbUnawareHider extends JBPanelWithEmptyText { + private final JComponent myDumbUnawareContent; public DumbUnawareHider(@NotNull JComponent dumbUnawareContent) { @@ -20,7 +36,6 @@ public final class DumbUnawareHider extends JBPanelWithEmptyText { add(dumbUnawareContent, BorderLayout.CENTER); } - @NotNull public JComponent getContent() { return myDumbUnawareContent; } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java b/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java index 77d7a1b66570..6006899df54e 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java @@ -474,15 +474,18 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { * */ public void setFirstComponent(@Nullable JComponent component) { - if (myFirstComponent == component) { - return; - } + if (myFirstComponent != component) { + if (myFirstComponent != null) { + remove(myFirstComponent); + } + myFirstComponent = component; + updateComponentTreeUI(myFirstComponent); - if (myFirstComponent != null) { - remove(myFirstComponent); + if (myFirstComponent != null) { + add(myFirstComponent); + myFirstComponent.invalidate(); + } } - myFirstComponent = component; - doAddComponent(component); } @Nullable @@ -490,20 +493,25 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { return myLastComponent; } + /** * Sets component which is located as the "second" split area. The method doesn't validate and * repaint the splitter. + * */ public void setLastComponent(@Nullable JComponent component) { - if (myLastComponent == component) { - return; - } + if (myLastComponent != component) { + if (myLastComponent != null) { + remove(myLastComponent); + } + myLastComponent = component; + updateComponentTreeUI(myLastComponent); - if (myLastComponent != null) { - remove(myLastComponent); + if (myLastComponent != null) { + add(myLastComponent); + myLastComponent.invalidate(); + } } - myLastComponent = component; - doAddComponent(component); } @Nullable @@ -511,12 +519,10 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { return myInnerComponent; } - private static void updateComponentTreeUI(@Nullable JComponent rootComponent) { - for (Component component : UIUtil.uiTraverser(rootComponent).postOrderDfsTraversal()) { - if (component instanceof JComponent) { - ((JComponent)component).updateUI(); - } - } + private static void updateComponentTreeUI(@Nullable JComponent component) { + UIUtil.uiTraverser(component).postOrderDfsTraversal(). + filter(c -> c instanceof JComponent). + forEach(c -> ((JComponent)c).updateUI()); } /** @@ -531,15 +537,13 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { if (myInnerComponent != null) { remove(myInnerComponent); } - myInnerComponent = component; - doAddComponent(component); - } - private void doAddComponent(@Nullable JComponent component) { - if (component != null) { - updateComponentTreeUI(component); - add(component); - component.invalidate(); + myInnerComponent = component; + updateComponentTreeUI(myInnerComponent); + + if (myInnerComponent != null) { + add(myInnerComponent); + myInnerComponent.invalidate(); } } @@ -633,6 +637,11 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { } private void _processMouseMotionEvent(MouseEvent e) { MouseEvent event = getTargetEvent(e); + if (event == null) { + myGlassPane.setCursor(null, myListener); + return; + } + processMouseMotionEvent(event); if (event.isConsumed()) { e.consume(); @@ -641,6 +650,11 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { private void _processMouseEvent(MouseEvent e) { MouseEvent event = getTargetEvent(e); + if (event == null) { + myGlassPane.setCursor(null, myListener); + return; + } + processMouseEvent(event); if (event.isConsumed()) { e.consume(); @@ -650,7 +664,7 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { private final MouseAdapter myListener = new MyMouseAdapter(); - private MouseEvent getTargetEvent(@NotNull MouseEvent e) { + private MouseEvent getTargetEvent(MouseEvent e) { return SwingUtilities.convertMouseEvent(e.getComponent(), e, this); } diff --git a/platform/platform-api/src/com/intellij/ui/content/Content.java b/platform/platform-api/src/com/intellij/ui/content/Content.java index fe535c1a2817..58d08f513f6b 100644 --- a/platform/platform-api/src/com/intellij/ui/content/Content.java +++ b/platform/platform-api/src/com/intellij/ui/content/Content.java @@ -50,9 +50,8 @@ public interface Content extends UserDataHolder, ComponentContainer { void setTabName(String tabName); String getTabName(); - String getToolwindowTitle(); - void setToolwindowTitle(String toolwindowTitle); + String getToolwindowTitle(); Disposable getDisposer(); @@ -60,6 +59,8 @@ public interface Content extends UserDataHolder, ComponentContainer { void setShouldDisposeContent(boolean value); + boolean shouldDisposeContent(); + String getDescription(); void setDescription(String description); diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/ToolWindowTabPainter.kt b/platform/platform-api/src/com/intellij/ui/tabs/impl/ToolWindowTabPainter.kt index c26782589ab5..6fd589c5238d 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/ToolWindowTabPainter.kt +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/ToolWindowTabPainter.kt @@ -7,7 +7,7 @@ import java.awt.Color import java.awt.Graphics2D import java.awt.Rectangle -internal class ToolWindowTabPainter: JBDefaultTabPainter(ToolWindowTabTheme()) { +class ToolWindowTabPainter: JBDefaultTabPainter(ToolWindowTabTheme()) { override fun paintTab(position: JBTabsPosition, g: Graphics2D, rect: Rectangle, @@ -18,8 +18,8 @@ internal class ToolWindowTabPainter: JBDefaultTabPainter(ToolWindowTabTheme()) rect.y += borderThickness rect.height -= borderThickness - if (position == JBTabsPosition.top) { - rect.height -= borderThickness + when (position) { + JBTabsPosition.top -> rect.height -= borderThickness } super.paintTab(position, g, rect, borderThickness, tabColor, active, hovered) diff --git a/platform/platform-api/src/com/intellij/ui/tabs/impl/themes/TabTheme.kt b/platform/platform-api/src/com/intellij/ui/tabs/impl/themes/TabTheme.kt index 210cf2b26f83..06584555a0ad 100644 --- a/platform/platform-api/src/com/intellij/ui/tabs/impl/themes/TabTheme.kt +++ b/platform/platform-api/src/com/intellij/ui/tabs/impl/themes/TabTheme.kt @@ -78,7 +78,7 @@ class EditorTabTheme : TabTheme { get() = JBUI.CurrentTheme.EditorTabs.inactiveColoredFileBackground() } -internal class ToolWindowTabTheme : DefaultTabTheme() { +class ToolWindowTabTheme : DefaultTabTheme() { override val background: Color? get() = null override val borderColor: Color @@ -104,7 +104,7 @@ internal class ToolWindowTabTheme : DefaultTabTheme() { get() = JBUI.CurrentTheme.ToolWindow.underlinedTabInactiveForeground() } -internal class DebuggerTabTheme : DefaultTabTheme() { +class DebuggerTabTheme : DefaultTabTheme() { override val underlineHeight: Int get() = JBUI.CurrentTheme.DebuggerTabs.underlineHeight() override val underlinedTabBackground: Color? diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButton.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButton.java index da1d5a0ffce9..b61cbffddbbd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButton.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/StripeButton.java @@ -313,7 +313,7 @@ public final class StripeButton extends AnchoredButton implements DataProvider { } void apply(@NotNull WindowInfo info) { - setSelected(info.isVisible()); + setSelected(info.isVisible() || info.isActive()); updateState(toolWindow); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java index f7d08e4d5912..4174b6e8f4de 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java @@ -13,14 +13,12 @@ import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; import com.intellij.ui.DoubleClickListener; import com.intellij.ui.PopupHandler; import com.intellij.ui.UIBundle; -import com.intellij.ui.layout.migLayout.MigLayoutBuilderKt; -import com.intellij.ui.layout.migLayout.patched.MigLayout; +import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.ui.tabs.impl.MorePopupAware; import com.intellij.ui.tabs.impl.SingleHeightTabs; import com.intellij.util.ui.*; import com.intellij.util.ui.accessibility.AccessibleContextUtil; -import net.miginfocom.layout.CC; -import net.miginfocom.layout.ConstraintParser; +import net.miginfocom.swing.MigLayout; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -37,7 +35,6 @@ import java.util.function.Supplier; * @author pegov */ public abstract class ToolWindowHeader extends JPanel implements UISettingsListener, DataProvider { - @NotNull private final ToolWindowContentUi contentUi; @NotNull private final Supplier myGearProducer; @NotNull @@ -53,19 +50,20 @@ public abstract class ToolWindowHeader extends JPanel implements UISettingsListe private ActionToolbar myToolbarWest; private final JPanel myWestPanel; - ToolWindowHeader(@NotNull ToolWindowImpl toolWindow, @NotNull ToolWindowContentUi contentUi, @NotNull Supplier gearProducer) { - super(new MigLayout(MigLayoutBuilderKt.createLayoutConstraints(0, 0).noVisualPadding().fill(), ConstraintParser.parseColumnConstraints("[grow][pref!]"))); - - this.contentUi = contentUi; + ToolWindowHeader(@NotNull ToolWindowImpl toolWindow, @NotNull Supplier gearProducer) { myGearProducer = gearProducer; AccessibleContextUtil.setName(this, "Tool Window Header"); myToolWindow = toolWindow; - myWestPanel = contentUi.getTabComponent(); - add(myWestPanel, new CC().grow()); - ToolWindowContentUi.initMouseListeners(myWestPanel, contentUi, true); + setLayout(new MigLayout("novisualpadding, ins 0, gap 0, fill", "[grow][pref!]")); + myWestPanel = new NonOpaquePanel(new MigLayout("filly, novisualpadding, ins 0, gap 0")); + + add(myWestPanel, "grow"); + myWestPanel.add(toolWindow.getContentUI().getTabComponent(), "growy"); + + ToolWindowContentUi.initMouseListeners(myWestPanel, toolWindow.getContentUI(), true); ActionManager actionManager = ActionManager.getInstance(); AnAction tabListAction = actionManager.getAction("TabList"); @@ -85,7 +83,8 @@ public abstract class ToolWindowHeader extends JPanel implements UISettingsListe myWestPanel.addMouseListener(new PopupHandler() { @Override public void invokePopup(final Component comp, final int x, final int y) { - contentUi.showContextMenu(comp, x, y, toolWindow.getPopupGroup(), contentUi.getContentManager().getSelectedContent()); + toolWindow.getContentUI() + .showContextMenu(comp, x, y, toolWindow.getPopupGroup(), toolWindow.getContentManager().getSelectedContent()); } }); myWestPanel.addMouseListener(new MouseAdapter() { @@ -140,7 +139,7 @@ public abstract class ToolWindowHeader extends JPanel implements UISettingsListe @Override public Object getData(@NotNull String dataId) { if (MorePopupAware.KEY.is(dataId) && myToolWindow instanceof ToolWindowImpl) { - return contentUi.getData(dataId); + return ((ToolWindowImpl)myToolWindow).getContentUI().getData(dataId); } return null; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowImpl.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowImpl.kt index 75e02b0d7b9e..d7b719c1f6d5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowImpl.kt @@ -10,10 +10,10 @@ import com.intellij.notification.EventLog import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.* import com.intellij.openapi.actionSystem.ex.ActionUtil +import com.intellij.openapi.actionSystem.impl.ActionManagerImpl import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.DumbAware -import com.intellij.openapi.project.DumbService import com.intellij.openapi.util.ActionCallback import com.intellij.openapi.util.BusyObject import com.intellij.openapi.util.Disposer @@ -22,22 +22,18 @@ import com.intellij.openapi.wm.ex.ToolWindowEx import com.intellij.openapi.wm.impl.content.ToolWindowContentUi import com.intellij.ui.LayeredIcon import com.intellij.ui.UIBundle -import com.intellij.ui.content.Content import com.intellij.ui.content.ContentManager import com.intellij.ui.content.ContentManagerListener import com.intellij.ui.content.impl.ContentImpl import com.intellij.ui.content.impl.ContentManagerImpl import com.intellij.ui.scale.JBUIScale -import com.intellij.util.ui.UIUtil import com.intellij.util.ui.update.Activatable import com.intellij.util.ui.update.UiNotifyConnector -import java.awt.BorderLayout import java.awt.Component import java.awt.Rectangle import java.awt.event.ComponentAdapter import java.awt.event.ComponentEvent import java.awt.event.InputEvent -import java.util.* import javax.swing.Icon import javax.swing.JComponent import javax.swing.JLabel @@ -49,7 +45,7 @@ private val LOG = logger() class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManagerImpl, val id: String, private val canCloseContent: Boolean, - private val dumbAware: Boolean, + private val canWorkInDumbMode: Boolean, component: JComponent?, private val parentDisposable: Disposable, windowInfo: WindowInfo, @@ -60,14 +56,16 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag var windowInfo: WindowInfo = windowInfo private set - private var contentUi: ToolWindowContentUi? = null + private val contentUi by lazy { + ToolWindowContentUi(this) + } private var decorator: InternalDecorator? = null private var hideOnEmptyContent = false var isPlaceholderMode = false - private var pendingContentManagerListeners: MutableList? = null + private val pendingContentManagerListeners: List? = null private val showing = object : BusyObject.Impl() { override fun isReady(): Boolean { @@ -84,7 +82,36 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag internal var icon: ToolWindowIcon? = null private val contentManager = lazy { - createContentManager() + val contentManager = ContentManagerImpl(contentUi, canCloseContent, toolWindowManager.project, parentDisposable) + + val contentComponent = contentManager.component + InternalDecorator.installFocusTraversalPolicy(contentComponent, LayoutFocusTraversalPolicy()) + Disposer.register(parentDisposable, UiNotifyConnector(contentComponent, object : Activatable { + override fun showNotify() { + showing.onReady() + } + })) + + val decorator = InternalDecorator(this) + this.decorator = decorator + decorator.applyWindowInfo(windowInfo) + decorator.addComponentListener(object : ComponentAdapter() { + override fun componentResized(e: ComponentEvent) { + toolWindowManager.resized(e.component as InternalDecorator) + } + }) + decorator.addContentComponent(canWorkInDumbMode, contentManager) + + toolWindowFocusWatcher = ToolWindowManagerImpl.ToolWindowFocusWatcher(this, contentManager.component) + + // after init, as it was before contentManager creation was changed to be lazy + pendingContentManagerListeners?.let { list -> + for (listener in list) { + contentManager.addContentManagerListener(listener) + } + } + + contentManager } init { @@ -96,92 +123,13 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag } } - internal fun getOrCreateDecoratorComponent(): JComponent { - ensureContentManagerInitialized() - return decorator!! - } - - private fun createContentManager(): ContentManagerImpl { - val contentUi = ToolWindowContentUi(this, windowInfo.contentUiType) - val contentManager = ContentManagerImpl(contentUi, canCloseContent, toolWindowManager.project, parentDisposable) - - UIUtil.putClientProperty(contentUi.component, UIUtil.NOT_IN_HIERARCHY_COMPONENTS, object : Iterable { - override fun iterator(): Iterator { - if (contentManager.contentCount == 0) { - return Collections.emptyIterator() - } - - return contentManager.contents - .asSequence() - .mapNotNull { content: Content -> - var last: JComponent? = null - var parent: Component? = content.component - while (parent != null) { - if (parent === contentUi.component || parent !is JComponent) { - return@mapNotNull null - } - last = parent - parent = parent.getParent() - } - last - } - .iterator() - } - }) - - val contentComponent = contentManager.component - InternalDecorator.installFocusTraversalPolicy(contentComponent, LayoutFocusTraversalPolicy()) - Disposer.register(parentDisposable, UiNotifyConnector(contentComponent, object : Activatable { - override fun showNotify() { - showing.onReady() - } - })) - - val decorator = InternalDecorator(this, contentUi) - this.decorator = decorator - - var decoratorChild = contentManager.component - if (!dumbAware) { - decoratorChild = DumbService.getInstance(toolWindowManager.project).wrapGently(decoratorChild, parentDisposable) - } - decorator.add(decoratorChild, BorderLayout.CENTER) - decorator.applyWindowInfo(windowInfo) - decorator.addComponentListener(object : ComponentAdapter() { - override fun componentResized(e: ComponentEvent) { - toolWindowManager.resized(e.component as InternalDecorator) - } - }) - - toolWindowFocusWatcher = ToolWindowManagerImpl.ToolWindowFocusWatcher(this, contentComponent) - - if (windowInfo.isVisible) { - createContentIfNeeded() - } - - // after init, as it was before contentManager creation was changed to be lazy - pendingContentManagerListeners?.let { list -> - pendingContentManagerListeners = null - for (listener in list) { - contentManager.addContentManagerListener(listener) - } - } - - return contentManager - } - internal fun applyWindowInfo(info: WindowInfo) { if (windowInfo == info) { return } windowInfo = info - val decorator = decorator - contentUi?.setType(info.contentUiType) - if (decorator != null) { - decorator.applyWindowInfo(info) - decorator.validate() - decorator.repaint() - } + decorator?.applyWindowInfo(info) } val decoratorComponent: JComponent? @@ -194,7 +142,6 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag toolWindowFocusWatcher?.setFocusedComponentImpl(component) } - @Deprecated(message = "Do not use.", level = DeprecationLevel.ERROR) fun getContentUI() = contentUi override fun getDisposable() = parentDisposable @@ -217,7 +164,19 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag override fun isActive(): Boolean { ApplicationManager.getApplication().assertIsDispatchThread() - return windowInfo.isVisible && decorator != null && toolWindowManager.activeToolWindowId == id + val frameHelper = toolWindowManager.getFrame() ?: return false + val frame = frameHelper.frame + if (!frame.isActive || toolWindowManager.isEditorComponentActive) { + return false + } + + val actionManager = ActionManager.getInstance() + if (actionManager is ActionManagerImpl && !actionManager.isActionPopupStackEmpty && !actionManager.isToolWindowContextMenuVisible) { + return false + } + else { + return windowInfo.isActive || (windowInfo.isVisible && (decorator?.isFocused(frameHelper.frame) ?: false)) + } } override fun getReady(requestor: Any): ActionCallback { @@ -303,22 +262,27 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag override fun getDecorator() = decorator!! + val isFocused: Boolean + get() { + return decorator?.isFocused(toolWindowManager.getFrame()?.frame ?: return false) ?: false + } + override fun setAdditionalGearActions(value: ActionGroup?) { additionalGearActions = value } override fun setTitleActions(vararg actions: AnAction) { - createContentIfNeeded() + contentManager.value decorator!!.setTitleActions(actions) } override fun setTabActions(vararg actions: AnAction) { - createContentIfNeeded() + contentManager.value decorator!!.setTabActions(actions) } fun setTabDoubleClickActions(vararg actions: AnAction) { - contentUi?.setTabDoubleClickActions(*actions) + contentUi.setTabDoubleClickActions(*actions) } override fun setAvailable(available: Boolean, runnable: Runnable?) { @@ -357,7 +321,7 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag } override fun getContentManager(): ContentManager { - createContentIfNeeded() + ensureContentInitialized() return contentManager.value } @@ -365,12 +329,6 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag if (contentManager.isInitialized()) { contentManager.value.addContentManagerListener(listener) } - else { - if (pendingContentManagerListeners == null) { - pendingContentManagerListeners = arrayListOf() - } - pendingContentManagerListeners!!.add(listener) - } } fun canCloseContents() = canCloseContent @@ -456,35 +414,17 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag override fun isDisposed() = contentManager.isInitialized() && contentManager.value.isDisposed - private fun ensureContentManagerInitialized() { - contentManager.value - } - - // if content manager due to some incorrect call was already initialized, content will be not created - internal fun scheduleContentInitializationIfNeeded() { - if (contentFactory != null) { - // todo use lazy loading (e.g. JBLoadingPanel) - createContentIfNeeded() - } - } - - @Suppress("DeprecatedCallableAddReplaceWith") - @Deprecated("Do not use. Tool window content will be initialized automatically.", level = DeprecationLevel.ERROR) fun ensureContentInitialized() { - createContentIfNeeded() - } - - internal fun createContentIfNeeded() { - val currentContentFactory = contentFactory ?: return - // clear it first to avoid SOE - this.contentFactory = null - if (contentManager.isInitialized()) { - contentManager.value.removeAllContents(false) + val currentContentFactory = contentFactory + if (currentContentFactory != null) { + // clear it first to avoid SOE + this.contentFactory = null + if (contentManager.isInitialized()) { + contentManager.value.removeAllContents(false) + } + contentManager.value + currentContentFactory.createToolWindowContent(toolWindowManager.project, this) } - else { - ensureContentManagerInitialized() - } - currentContentFactory.createToolWindowContent(toolWindowManager.project, this) } override fun getHelpId() = helpId @@ -494,8 +434,9 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag } override fun showContentPopup(inputEvent: InputEvent) { - // called only when tool window is already opened, so, content should be already created - ToolWindowContentUi.toggleContentPopup(contentUi!!, contentManager.value) + // create before access contentUi + val contentManager = contentManager.value + ToolWindowContentUi.toggleContentPopup(contentUi, contentManager) } @JvmOverloads @@ -583,7 +524,7 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag private inner class ResizeActionGroup : ActionGroup(ActionsBundle.groupText("ResizeToolWindowGroup"), true), DumbAware { private val children by lazy> { // force creation - createContentIfNeeded() + contentManager.value val component = decorator val toolWindow = this@ToolWindowImpl arrayOf( @@ -617,7 +558,7 @@ class ToolWindowImpl internal constructor(val toolWindowManager: ToolWindowManag } override fun update(e: AnActionEvent) { - hadSeveralContents = hadSeveralContents || (contentManager.isInitialized() && contentManager.value.contentCount > 1) + hadSeveralContents = hadSeveralContents || getContentManager().contentCount > 1 super.update(e) e.presentation.isVisible = hadSeveralContents } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.kt b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.kt index a5f770260eb5..acd2bc0c8da8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.kt +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.kt @@ -50,6 +50,7 @@ import com.intellij.openapi.wm.ex.ToolWindowManagerEx import com.intellij.openapi.wm.ex.ToolWindowManagerListener import com.intellij.openapi.wm.ex.WindowManagerEx import com.intellij.openapi.wm.impl.commands.RequestFocusInToolWindowCommand +import com.intellij.openapi.wm.impl.content.ToolWindowContentUi import com.intellij.ui.BalloonImpl import com.intellij.ui.ComponentUtil import com.intellij.ui.GuiUtils @@ -622,6 +623,9 @@ open class ToolWindowManagerImpl(val project: Project) : ToolWindowManagerEx(), if (parent is InternalDecorator) { return parent.toolWindow.id } + else if (parent is ToolWindowContentUi) { + return parent.toolWindowId + } parent = parent.parent } @@ -772,7 +776,7 @@ open class ToolWindowManagerImpl(val project: Project) : ToolWindowManagerEx(), private fun doHide(entry: ToolWindowEntry, info: WindowInfoImpl, dirtyMode: Boolean, hideSide: Boolean = false, moveFocus: Boolean = true): Boolean { // info.isActive is not reliable (not set to false on focus lost) - val wasActive = entry.toolWindow.isActive + val wasActive = entry.toolWindow.isFocused if (!wasActive && !info.isVisible) { return false @@ -853,6 +857,8 @@ open class ToolWindowManagerImpl(val project: Project) : ToolWindowManagerEx(), } private fun doShowWindow(entry: ToolWindowEntry, info: WindowInfo, dirtyMode: Boolean) { + entry.toolWindow.ensureContentInitialized() + if (entry.readOnlyWindowInfo.type == ToolWindowType.FLOATING) { addFloatingDecorator(entry, info) } @@ -895,14 +901,12 @@ open class ToolWindowManagerImpl(val project: Project) : ToolWindowManagerEx(), } } - toolWindowPane!!.addDecorator(entry.toolWindow.getOrCreateDecoratorComponent(), info, dirtyMode, this) + toolWindowPane!!.addDecorator(entry.toolWindow.decoratorComponent!!, info, dirtyMode, this) // remove tool window from the SideStack if (isStackEnabled) { sideStack.remove(entry.id) } } - - entry.toolWindow.scheduleContentInitializationIfNeeded() } override fun registerToolWindow(task: RegisterToolWindowTask): ToolWindowImpl { @@ -1379,7 +1383,7 @@ open class ToolWindowManagerImpl(val project: Project) : ToolWindowManagerEx(), private fun hideIfNeededAndShowAfterTask(entry: ToolWindowEntry, info: WindowInfoImpl, task: () -> Unit) { val wasVisible = entry.readOnlyWindowInfo.isVisible - val wasFocused = entry.toolWindow.isActive + val wasFocused = entry.toolWindow.isFocused if (wasVisible) { doHide(entry, info, dirtyMode = true, moveFocus = false) } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java index a14ee155779e..df23fd3f1421 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java @@ -187,6 +187,9 @@ public final class ToolWindowsPane extends JBLayeredPane implements UISettingsLi } /** + * Creates command which shows tool window with specified set of parameters. + * Command uses cloned copy of passed {@code info} object. + * * @param dirtyMode if {@code true} then JRootPane will not be validated and repainted after adding * the decorator. Moreover in this (dirty) mode animation doesn't work. */ @@ -288,7 +291,7 @@ public final class ToolWindowsPane extends JBLayeredPane implements UISettingsLi } } - private void setComponent(@Nullable JComponent component, @NotNull ToolWindowAnchor anchor, float weight) { + private void setComponent(@Nullable JComponent component, @NotNull ToolWindowAnchor anchor, final float weight) { if (ToolWindowAnchor.TOP == anchor) { verticalSplitter.setFirstComponent(component); verticalSplitter.setFirstSize((int)(layeredPane.getHeight() * weight)); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/BaseLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/BaseLabel.java index 96d9ba9eb8fa..bdbb9f99d4c7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/BaseLabel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/BaseLabel.java @@ -12,7 +12,6 @@ import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.WatermarkIcon; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.accessibility.AccessibleContext; @@ -28,7 +27,7 @@ public class BaseLabel extends JLabel { private Color myPassiveFg; private boolean myBold; - public BaseLabel(@NotNull ToolWindowContentUi ui, boolean bold) { + public BaseLabel(ToolWindowContentUi ui, boolean bold) { myUi = ui; setOpaque(false); myBold = bold; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ComboContentLayout.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ComboContentLayout.java index 6a6a910fc6d0..47de0081741e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ComboContentLayout.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ComboContentLayout.java @@ -34,7 +34,7 @@ final class ComboContentLayout extends ContentLayout { @Override public void layout() { - Rectangle bounds = myUi.getTabComponent().getBounds(); + Rectangle bounds = myUi.getBounds(); Dimension idSize = isIdVisible() ? myIdLabel.getPreferredSize() : JBUI.emptySize(); int eachX = 0; @@ -70,6 +70,9 @@ final class ComboContentLayout extends ContentLayout { g.drawLine(r.width - 1, 0, r.width - 1, r.height); } + @Override + public void paintChildren(Graphics g) { } + @Override public void update() { updateIdLabel(myIdLabel); @@ -78,17 +81,17 @@ final class ComboContentLayout extends ContentLayout { @Override public void rebuild() { - myUi.getTabComponent().removeAll(); + myUi.removeAll(); - myUi.getTabComponent().add(myIdLabel); + myUi.add(myIdLabel); ToolWindowContentUi.initMouseListeners(myIdLabel, myUi, true); - myUi.getTabComponent().add(myComboLabel); + myUi.add(myComboLabel); ToolWindowContentUi.initMouseListeners(myComboLabel, myUi, false); } boolean isToDrawCombo() { - ContentManager manager = myUi.getContentManager(); + ContentManager manager = myUi.contentManager; return manager != null && manager.getContentCount() > 1; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentComboLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentComboLabel.java index f4d285b627e5..00eb3e575bc6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentComboLabel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentComboLabel.java @@ -3,7 +3,6 @@ package com.intellij.openapi.wm.impl.content; import com.intellij.ui.Gray; import com.intellij.ui.content.Content; -import com.intellij.ui.content.ContentManager; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.accessibility.ScreenReader; @@ -45,7 +44,7 @@ final class ContentComboLabel extends BaseLabel { @Override public void keyPressed(KeyEvent e) { if (e.getModifiers() == 0 && e.getKeyCode() == KeyEvent.VK_SPACE) { - ToolWindowContentUi.toggleContentPopup(myUi, myUi.getContentManager()); + ToolWindowContentUi.toggleContentPopup(myUi, myUi.contentManager); } super.keyPressed(e); } @@ -58,7 +57,7 @@ final class ContentComboLabel extends BaseLabel { super.processMouseEvent(e); if (UIUtil.isActionClick(e)) { - ToolWindowContentUi.toggleContentPopup(myUi, myUi.getContentManager()); + ToolWindowContentUi.toggleContentPopup(myUi, myUi.contentManager); } } @@ -110,8 +109,7 @@ final class ContentComboLabel extends BaseLabel { @Nullable @Override public Content getContent() { - ContentManager contentManager = myUi.getContentManager(); - return contentManager == null ? null : contentManager.getSelectedContent(); + return myUi.contentManager == null ? null : myUi.contentManager.getSelectedContent(); } @Override @@ -147,7 +145,7 @@ final class ContentComboLabel extends BaseLabel { @Override public boolean doAccessibleAction(int index) { if (index == 0) { - ToolWindowContentUi.toggleContentPopup(myUi, myUi.getContentManager()); + ToolWindowContentUi.toggleContentPopup(myUi, myUi.contentManager); return true; } else { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentLayout.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentLayout.java index ec06b7c127a7..be37d6a6011f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentLayout.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentLayout.java @@ -13,6 +13,8 @@ import javax.swing.*; import java.awt.*; abstract class ContentLayout { + static final int TAB_ARC = 2; + ToolWindowContentUi myUi; BaseLabel myIdLabel; @@ -28,6 +30,8 @@ abstract class ContentLayout { public abstract void paintComponent(Graphics g); + public abstract void paintChildren(Graphics g); + public abstract void update(); public abstract void rebuild(); @@ -50,7 +54,7 @@ abstract class ContentLayout { } private String getTitleSuffix() { - ContentManager manager = myUi.getContentManager(); + ContentManager manager = myUi.contentManager; switch (manager == null ? 0 : manager.getContentCount()) { case 0: return null; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java index 903d2fe7deaa..16d46d626c5e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ContentTabLabel.java @@ -23,6 +23,7 @@ import com.intellij.util.ui.TimedDeadzone; import com.intellij.util.ui.UIUtilities; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.EmptyBorder; @@ -104,6 +105,7 @@ class ContentTabLabel extends BaseLabel { } String toolText = icon.getTooltip(); + if (toolText != null && !toolText.isEmpty()) { IdeTooltip tooltip = new IdeTooltip(this, icon.getCenterPoint(), new JLabel(toolText)); currentIconTooltip = new CurrentTooltip(IdeTooltipManager.getInstance().show(tooltip, false, false), icon); @@ -116,6 +118,7 @@ class ContentTabLabel extends BaseLabel { IdeTooltip tooltip = new IdeTooltip(this, getMousePosition(), new JLabel(myText)); currentIconTooltip = new CurrentTooltip(IdeTooltipManager.getInstance().show(tooltip, false, false), null); } + } private void hideCurrentTooltip() { @@ -127,12 +130,15 @@ class ContentTabLabel extends BaseLabel { private final BaseButtonBehavior behavior = new BaseButtonBehavior(this) { @Override - protected void execute(@NotNull MouseEvent e) { - for (AdditionalIcon icon : myAdditionalIcons) { - if (mouseOverIcon(icon)) { - icon.getAction().run(); - return; - } + protected void execute(final MouseEvent e) { + + Optional first = myAdditionalIcons.stream() + .filter(icon -> mouseOverIcon(icon)) + .map(icon -> icon.getAction()).findFirst(); + + if (first.isPresent()) { + first.get().run(); + return; } selectContent(); @@ -352,10 +358,10 @@ class ContentTabLabel extends BaseLabel { @NotNull private ContentManager getContentManager() { - return myUi.getContentManager(); + return myUi.myWindow.getContentManager(); } - @NotNull + @Nullable @Override public Content getContent() { return myContent; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java index 9ae953a9ae10..781a91dc0aa1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabContentLayout.java @@ -39,10 +39,10 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { List myDoubleClickActions = new ArrayList<>(); - TabContentLayout(@NotNull ToolWindowContentUi ui) { + TabContentLayout(ToolWindowContentUi ui) { super(ui); - new BaseButtonBehavior(myUi.getTabComponent()) { + new BaseButtonBehavior(myUi) { @Override protected void execute(final MouseEvent e) { if (!myUi.isCurrent(TabContentLayout.this)) return; @@ -65,7 +65,7 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { } }; - ContentManager contentManager = myUi.getContentManager(); + ContentManager contentManager = myUi.contentManager; for (int i = 0; i < contentManager.getContentCount(); i++) { contentAdded(new ContentManagerEvent(this, contentManager.getContent(i), i)); } @@ -99,14 +99,14 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { List tabs = ContainerUtil.filter(myTabs, myLastLayout.toDrop::contains); final List contentsToShow = ContainerUtil.map(tabs, ContentTabLabel::getContent); final SelectContentStep step = new SelectContentStep(contentsToShow); - RelativePoint point = new RelativePoint(myUi.getTabComponent(), new Point(rect.x, rect.y + rect.height)); + RelativePoint point = new RelativePoint(myUi, new Point(rect.x, rect.y + rect.height)); JBPopupFactory.getInstance().createListPopup(step).show(point); } @Override public void layout() { - Rectangle bounds = myUi.getTabComponent().getBounds(); - ContentManager manager = myUi.getContentManager(); + Rectangle bounds = myUi.getBounds(); + ContentManager manager = myUi.contentManager; LayoutData data = new LayoutData(myUi); data.eachX = TAB_LAYOUT_START; @@ -137,6 +137,7 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { } } + if (data.fullLayout) { for (ContentTabLabel eachTab : myTabs) { final Dimension eachSize = eachTab.getPreferredSize(); @@ -199,7 +200,8 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { data.moreRect = null; } - Rectangle moreRect = data.moreRect == null ? null : new Rectangle(data.eachX, 0, /*getMoreToolbarWidth()*/16+MORE_ICON_BORDER, bounds.height); + final Rectangle moreRect = data.moreRect == null ? null : new Rectangle(data.eachX, 0, /*getMoreToolbarWidth()*/16+MORE_ICON_BORDER, bounds.height); + myUi.isResizableArea = p -> moreRect == null || !moreRect.contains(p); myLastLayout = data; if (toolbarUpdateNeeded) { @@ -218,7 +220,7 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { } } - ContentManager contentManager = myUi.getContentManager(); + ContentManager contentManager = myUi.contentManager; Content selected = contentManager.getSelectedContent(); if (selected == null && contentManager.getContents().length > 0) { selected = contentManager.getContents()[0]; @@ -235,16 +237,14 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { } boolean isToDrawTabs() { - int size = myTabs.size(); - if (size > 1) { - return true; - } - else if (size == 1) { - return !StringUtil.isEmpty(myTabs.get(0).getContent().getToolwindowTitle()); - } - else { + if(myTabs.size() > 1) return true; + + if(myTabs.size() == 1) { + String title = myTabs.get(0).getContent().getToolwindowTitle(); + return !StringUtil.isEmpty(title); + } + return false; - } } static class LayoutData { @@ -263,12 +263,12 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { public int contentCount; LayoutData(ToolWindowContentUi ui) { - layoutSize = ui.getTabComponent().getSize(); - contentCount = ui.getContentManager().getContentCount(); + layoutSize = ui.getSize(); + contentCount = ui.contentManager.getContentCount(); } } - private final JBTabPainter tabPainter = JBTabPainter.getTOOL_WINDOW(); + private JBTabPainter tabPainter = JBTabPainter.getTOOL_WINDOW(); @Override public void paintComponent(Graphics g) { @@ -292,6 +292,11 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { g2d.dispose(); } + @Override + public void paintChildren(Graphics g) { + if (!isToDrawTabs()) return; + } + @Override public void update() { for (ContentTabLabel each : myTabs) { @@ -303,13 +308,13 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { @Override public void rebuild() { - myUi.getTabComponent().removeAll(); + myUi.removeAll(); - myUi.getTabComponent().add(myIdLabel); + myUi.add(myIdLabel); ToolWindowContentUi.initMouseListeners(myIdLabel, myUi, true); for (ContentTabLabel each : myTabs) { - myUi.getTabComponent().add(each); + myUi.add(each); ToolWindowContentUi.initMouseListeners(each, myUi, false); } } @@ -354,7 +359,7 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware { @Override public void showContentPopup(ListPopup listPopup) { - Content selected = myUi.getContentManager().getSelectedContent(); + Content selected = myUi.contentManager.getSelectedContent(); if (selected != null) { ContentTabLabel tab = myContent2Tabs.get(selected); listPopup.showUnderneathOf(tab); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java index f16f85063ee7..8405f0a0fb9b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/TabbedContentTabLabel.java @@ -8,6 +8,7 @@ import com.intellij.reference.SoftReference; import com.intellij.ui.content.TabbedContent; import com.intellij.util.ui.JBUI; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.awt.*; import java.lang.ref.Reference; @@ -17,7 +18,8 @@ import java.util.List; /** * @author Konstantin Bulenkov */ -public final class TabbedContentTabLabel extends ContentTabLabel { +public class TabbedContentTabLabel extends ContentTabLabel { + private final TabbedContent myContent; private Reference myPopupReference = null; @@ -78,7 +80,7 @@ public final class TabbedContentTabLabel extends ContentTabLabel { return hasMultipleTabs(); } - @NotNull + @Nullable @Override public Runnable getAction() { return () -> selectContent(); @@ -97,7 +99,7 @@ public final class TabbedContentTabLabel extends ContentTabLabel { } } - @NotNull + @Nullable @Override public TabbedContent getContent() { return myContent; diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java index ad8f4c28818c..404b3963d87b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/content/ToolWindowContentUi.java @@ -13,6 +13,7 @@ import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.ThreeComponentsSplitter; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; @@ -23,11 +24,10 @@ import com.intellij.ui.PopupHandler; import com.intellij.ui.content.*; import com.intellij.ui.content.tabs.PinToolwindowTabAction; import com.intellij.ui.content.tabs.TabbedContentAction; -import com.intellij.ui.layout.migLayout.MigLayoutBuilderKt; -import com.intellij.ui.layout.migLayout.patched.MigLayout; import com.intellij.ui.tabs.impl.MorePopupAware; import com.intellij.util.Alarm; import com.intellij.util.ContentUtilEx; +import com.intellij.util.containers.JBIterable; import com.intellij.util.containers.Predicate; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.LocationOnDragTracker; @@ -43,48 +43,73 @@ import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; import java.util.List; -public final class ToolWindowContentUi implements ContentUI, DataProvider { +public final class ToolWindowContentUi extends JPanel implements ContentUI, DataProvider, UISettingsListener { // when client property is put in toolwindow component, hides toolwindow label public static final String HIDE_ID_LABEL = "HideIdLabel"; private static final String TOOLWINDOW_UI_INSTALLED = "ToolWindowUiInstalled"; - private ContentManager contentManager; + ContentManager contentManager; - public ContentManager getContentManager() { - return contentManager; - } + final JPanel myContent = new JPanel(new BorderLayout()); + ToolWindowImpl myWindow; - private final JPanel myContent = new JPanel(new BorderLayout()); - final ToolWindowImpl myWindow; + TabbedContentAction.CloseAllAction myCloseAllAction; + TabbedContentAction.MyNextTabAction myNextTabAction; + TabbedContentAction.MyPreviousTabAction myPreviousTabAction; - private TabbedContentAction.CloseAllAction myCloseAllAction; - private TabbedContentAction.MyNextTabAction myNextTabAction; - private TabbedContentAction.MyPreviousTabAction myPreviousTabAction; + ShowContentAction myShowContent; - private ShowContentAction myShowContent; + TabContentLayout myTabsLayout = new TabContentLayout(this); + ContentLayout myComboLayout = new ComboContentLayout(this); - private final TabContentLayout myTabsLayout; - private ContentLayout myComboLayout; - - private ToolWindowContentUiType myType; + private ToolWindowContentUiType myType = ToolWindowContentUiType.TABBED; public Predicate isResizableArea = p -> true; - private final JPanel tabComponent = new TabPanel(); - - @NotNull - public JPanel getTabComponent() { - return tabComponent; - } - - public ToolWindowContentUi(@NotNull ToolWindowImpl window, @NotNull ToolWindowContentUiType contentUiType) { - myType = contentUiType; - myTabsLayout = new TabContentLayout(this); + public ToolWindowContentUi(@NotNull ToolWindowImpl window) { myWindow = window; myContent.setOpaque(false); myContent.setFocusable(false); + setOpaque(false); + + setBorder(JBUI.Borders.emptyRight(2)); + + // InternalDecorator adds myContent right after "this" (via ToolWindowHeader) + // also myContent is never removed (can be invisible due to DumbAwareHider) + UIUtil.putClientProperty(myContent, UIUtil.NOT_IN_HIERARCHY_COMPONENTS, new Iterable() { + @Override + public Iterator iterator() { + if (contentManager == null || contentManager.getContentCount() == 0) { + return Collections.emptyIterator(); + } + return JBIterable.of(contentManager.getContents()) + .map(content -> { + JComponent last = null; + for (Component c : UIUtil.uiParents(content.getComponent(), false)) { + if (c == contentManager.getComponent() || !(c instanceof JComponent)) return null; + last = (JComponent)c; + } + return last; + }) + .filter(Conditions.notNull()) + .iterator(); + } + }); + } + + @NotNull + public String getToolWindowId() { + return myWindow.getId(); + } + + @Override + public void uiSettingsChanged(@NotNull UISettings uiSettings) { + revalidate(); + repaint(); } private boolean isResizeable() { @@ -132,15 +157,7 @@ public final class ToolWindowContentUi implements ContentUI, DataProvider { @NotNull private ContentLayout getCurrentLayout() { - if (myType == ToolWindowContentUiType.TABBED) { - return myTabsLayout; - } - else { - if (myComboLayout == null) { - myComboLayout = new ComboContentLayout(this); - } - return myComboLayout; - } + return myType == ToolWindowContentUiType.TABBED ? myTabsLayout : myComboLayout; } @Override @@ -148,6 +165,10 @@ public final class ToolWindowContentUi implements ContentUI, DataProvider { return myContent; } + public JComponent getTabComponent() { + return this; + } + @Override public void setManager(@NotNull ContentManager manager) { if (contentManager != null) { @@ -192,7 +213,7 @@ public final class ToolWindowContentUi implements ContentUI, DataProvider { } }); - initMouseListeners(tabComponent, this, true); + initMouseListeners(this, this, true); rebuild(); @@ -227,19 +248,57 @@ public final class ToolWindowContentUi implements ContentUI, DataProvider { getCurrentLayout().rebuild(); getCurrentLayout().update(); - tabComponent.revalidate(); - tabComponent.repaint(); + revalidate(); + repaint(); if (contentManager != null && contentManager.getContentCount() == 0 && myWindow.isToHideOnEmptyContent()) { myWindow.hide(null); } } + @Override + public void doLayout() { + getCurrentLayout().layout(); + } + + @Override + protected void paintComponent(final Graphics g) { + super.paintComponent(g); + getCurrentLayout().paintComponent(g); + } + + @Override + protected void paintChildren(final Graphics g) { + super.paintChildren(g); + getCurrentLayout().paintChildren(g); + } + + @Override + public Dimension getMinimumSize() { + Insets insets = getInsets(); + return new Dimension(insets.left + insets.right + getCurrentLayout().getMinimumWidth(), super.getMinimumSize().height); + } + + @Override + public Dimension getPreferredSize() { + Dimension size = new Dimension(); + size.height = 0; + size.width = TabContentLayout.TAB_LAYOUT_START + getInsets().left + getInsets().right; + for (int i = 0; i < getComponentCount(); i++) { + final Component each = getComponent(i); + size.height = Math.max(each.getPreferredSize().height, size.height); + size.width += each.getPreferredSize().width; + } + + size.width = Math.max(size.width, getMinimumSize().width); + return size; + } + private void update() { getCurrentLayout().update(); - tabComponent.revalidate(); - tabComponent.repaint(); + revalidate(); + repaint(); } @Override @@ -303,9 +362,7 @@ public final class ToolWindowContentUi implements ContentUI, DataProvider { final Ref myDragTracker = Ref.create(); private Component getActualSplitter() { - if (!allowResize || !ui.isResizeable()) { - return null; - } + if (!allowResize || !ui.isResizeable()) return null; Component component = c; Component parent = component.getParent(); @@ -570,8 +627,7 @@ public final class ToolWindowContentUi implements ContentUI, DataProvider { } else if (CloseAction.CloseTarget.KEY.is(dataId)) { return computeCloseTarget(); - } - else if (MorePopupAware.KEY.is(dataId)) { + } else if (MorePopupAware.KEY.is(dataId)) { ContentLayout layout = getCurrentLayout(); return (layout instanceof TabContentLayout) ? layout : null; } @@ -628,51 +684,4 @@ public final class ToolWindowContentUi implements ContentUI, DataProvider { new Alarm(Alarm.ThreadToUse.SWING_THREAD, popup).addRequest(() -> popup.handleSelect(false), 50); } } - - private final class TabPanel extends JPanel implements UISettingsListener { - private TabPanel() { - super(new MigLayout(MigLayoutBuilderKt.createLayoutConstraints(0, 0).noVisualPadding().fillY())); - - setOpaque(false); - setBorder(JBUI.Borders.emptyRight(2)); - } - - @Override - public void uiSettingsChanged(@NotNull UISettings uiSettings) { - revalidate(); - repaint(); - } - - @Override - public void doLayout() { - getCurrentLayout().layout(); - } - - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - getCurrentLayout().paintComponent(g); - } - - @Override - public Dimension getMinimumSize() { - Insets insets = getInsets(); - return new Dimension(insets.left + insets.right + getCurrentLayout().getMinimumWidth(), super.getMinimumSize().height); - } - - @Override - public Dimension getPreferredSize() { - Dimension size = new Dimension(); - size.height = 0; - size.width = TabContentLayout.TAB_LAYOUT_START + getInsets().left + getInsets().right; - for (int i = 0; i < getComponentCount(); i++) { - final Component each = getComponent(i); - size.height = Math.max(each.getPreferredSize().height, size.height); - size.width += each.getPreferredSize().width; - } - - size.width = Math.max(size.width, getMinimumSize().width); - return size; - } - } } diff --git a/platform/platform-impl/src/com/intellij/ui/content/impl/ContentImpl.java b/platform/platform-impl/src/com/intellij/ui/content/impl/ContentImpl.java index 1959373c8652..7fbf8524cfa3 100644 --- a/platform/platform-impl/src/com/intellij/ui/content/impl/ContentImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/content/impl/ContentImpl.java @@ -153,7 +153,8 @@ public class ContentImpl extends UserDataHolderBase implements Content { @Override public String getToolwindowTitle() { - return myToolwindowTitle == null ? myDisplayName : myToolwindowTitle; + if (myToolwindowTitle != null) return myToolwindowTitle; + return myDisplayName; } @Override @@ -171,6 +172,11 @@ public class ContentImpl extends UserDataHolderBase implements Content { myShouldDisposeContent = value; } + @Override + public boolean shouldDisposeContent() { + return myShouldDisposeContent; + } + @Override public String getDescription() { return myDescription; diff --git a/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java index c905fbe28616..3e2458122d91 100644 --- a/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java @@ -168,7 +168,7 @@ public class ContentManagerImpl implements ContentManager, PropertyChangeListene @Override public boolean removeContent(@NotNull Content content, boolean dispose) { - boolean wasFocused = UIUtil.isFocusAncestor(content.getComponent()); + boolean wasFocused = content.getComponent() != null && UIUtil.isFocusAncestor(content.getComponent()); return removeContent(content, dispose, wasFocused, false).isDone(); } diff --git a/platform/util/ui/src/com/intellij/util/ui/UIUtil.java b/platform/util/ui/src/com/intellij/util/ui/UIUtil.java index c81a3c9f3f9c..2ce9788044e0 100644 --- a/platform/util/ui/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/ui/src/com/intellij/util/ui/UIUtil.java @@ -2642,6 +2642,7 @@ public final class UIUtil { return result; }); + public static void scrollListToVisibleIfNeeded(@NotNull final JList list) { SwingUtilities.invokeLater(() -> { final int selectedIndex = list.getSelectedIndex(); diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalView.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalView.java index 16b392258568..526d79774aa2 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalView.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/TerminalView.java @@ -161,9 +161,11 @@ public class TerminalView { } @Nullable - private JBTerminalWidget createNewSession(@NotNull AbstractTerminalRunner terminalRunner, @Nullable TerminalTabState tabState, boolean requestFocus) { + private JBTerminalWidget createNewSession(@NotNull AbstractTerminalRunner terminalRunner, @Nullable TerminalTabState tabState, boolean requestFocus) { ToolWindow window = ToolWindowManager.getInstance(myProject).getToolWindow(TerminalToolWindowFactory.TOOL_WINDOW_ID); if (window != null && window.isAvailable()) { + // ensure TerminalToolWindowFactory.createToolWindowContent gets called + ((ToolWindowImpl)window).ensureContentInitialized(); Content content = createNewTab(null, terminalRunner, myToolWindow, tabState, requestFocus); window.activate(null); return Objects.requireNonNull(getWidgetByContent(content)); @@ -348,16 +350,16 @@ public class TerminalView { public void openTerminalIn(@Nullable VirtualFile fileToOpen) { ToolWindow window = ToolWindowManager.getInstance(myProject).getToolWindow(TerminalToolWindowFactory.TOOL_WINDOW_ID); - if (window == null || !window.isAvailable()) { - return; + if (window != null && window.isAvailable()) { + // ensure TerminalToolWindowFactory.createToolWindowContent gets called + ((ToolWindowImpl)window).ensureContentInitialized(); + TerminalTabState state = new TerminalTabState(); + if (fileToOpen != null) { + state.myWorkingDirectory = fileToOpen.getPath(); + } + createNewSession(myTerminalRunner, state); + window.activate(null); } - - TerminalTabState state = new TerminalTabState(); - if (fileToOpen != null) { - state.myWorkingDirectory = fileToOpen.getPath(); - } - createNewSession(myTerminalRunner, state); - window.activate(null); } @Nullable