mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
New run/debug tool window UI (IJ-CR-10839):
* Added a new interface SingleContentSupplier.kt * Added a new tool window header layout * Clean up previous new debugger layout * Implement new run layout GitOrigin-RevId: 54e1277131e8463dcdf457131a674559eb22be1a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1f6cbe81b3
commit
1905e9712b
@@ -9,11 +9,15 @@ import com.intellij.execution.configurations.RunConfigurationBase;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.ui.*;
|
||||
import com.intellij.execution.ui.layout.PlaceInGrid;
|
||||
import com.intellij.execution.ui.layout.impl.RunnerLayoutUiImpl;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.wm.impl.content.SingleContentSupplier;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.terminal.TerminalExecutionConsole;
|
||||
import com.intellij.ui.content.Content;
|
||||
@@ -38,6 +42,8 @@ public final class RunContentBuilder extends RunTab {
|
||||
myUi.getOptions().setMoveToGridActionEnabled(false).setMinimizeActionEnabled(false);
|
||||
}
|
||||
|
||||
private @Nullable SingleContentSupplier mySupplier;
|
||||
|
||||
@NotNull
|
||||
public static ExecutionEnvironment fix(@NotNull ExecutionEnvironment environment, @Nullable ProgramRunner runner) {
|
||||
if (runner == null || runner.equals(environment.getRunner())) {
|
||||
@@ -82,7 +88,17 @@ public final class RunContentBuilder extends RunTab {
|
||||
// clear console toolbar actions to remove the console toolbar
|
||||
consoleContent.setActions(new DefaultActionGroup(), ActionPlaces.RUNNER_TOOLBAR, console.getComponent());
|
||||
}
|
||||
myUi.getOptions().setLeftToolbar(createActionToolbar(contentDescriptor, consoleActionsToMerge), ActionPlaces.RUNNER_TOOLBAR);
|
||||
ActionGroup toolbar = createActionToolbar(contentDescriptor, consoleActionsToMerge);
|
||||
if (Registry.is("debugger.new.tool.window.layout")) {
|
||||
mySupplier = new RunTabSupplier(toolbar);
|
||||
if (myUi instanceof RunnerLayoutUiImpl) {
|
||||
((RunnerLayoutUiImpl)myUi).setLeftToolbarVisible(false);
|
||||
}
|
||||
myUi.getOptions().setTopLeftToolbar(toolbar, ActionPlaces.RUNNER_TOOLBAR);
|
||||
|
||||
} else {
|
||||
myUi.getOptions().setLeftToolbar(toolbar, ActionPlaces.RUNNER_TOOLBAR);
|
||||
}
|
||||
|
||||
if (profile instanceof RunConfigurationBase) {
|
||||
if (console instanceof ObservableConsoleView && !ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
@@ -97,6 +113,11 @@ public final class RunContentBuilder extends RunTab {
|
||||
return contentDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable SingleContentSupplier getSupplier() {
|
||||
return mySupplier;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getRunnerType(@Nullable ExecutionConsole console) {
|
||||
if (console instanceof ExecutionConsoleEx) {
|
||||
@@ -134,12 +155,16 @@ public final class RunContentBuilder extends RunTab {
|
||||
|
||||
@NotNull
|
||||
private ActionGroup createActionToolbar(@NotNull RunContentDescriptor contentDescriptor, AnAction @NotNull [] consoleActions) {
|
||||
boolean isNewLayout = Registry.is("debugger.new.tool.window.layout");
|
||||
|
||||
final DefaultActionGroup actionGroup = new DefaultActionGroup();
|
||||
actionGroup.add(ActionManager.getInstance().getAction(IdeActions.ACTION_RERUN));
|
||||
final AnAction[] actions = contentDescriptor.getRestartActions();
|
||||
actionGroup.addAll(actions);
|
||||
actionGroup.add(new CreateAction(AllIcons.General.Settings));
|
||||
actionGroup.addSeparator();
|
||||
if (!isNewLayout) {
|
||||
actionGroup.add(new CreateAction(AllIcons.General.Settings));
|
||||
actionGroup.addSeparator();
|
||||
}
|
||||
|
||||
actionGroup.add(ActionManager.getInstance().getAction(IdeActions.ACTION_STOP_PROGRAM));
|
||||
actionGroup.addAll(myExecutionResult.getActions());
|
||||
@@ -148,6 +173,10 @@ public final class RunContentBuilder extends RunTab {
|
||||
actionGroup.addAll(consoleActions);
|
||||
}
|
||||
|
||||
if (isNewLayout) {
|
||||
actionGroup.addSeparator();
|
||||
}
|
||||
|
||||
for (AnAction anAction : myRunnerActions) {
|
||||
if (anAction != null) {
|
||||
actionGroup.add(anAction);
|
||||
@@ -157,10 +186,27 @@ public final class RunContentBuilder extends RunTab {
|
||||
}
|
||||
}
|
||||
|
||||
actionGroup.addSeparator();
|
||||
actionGroup.add(myUi.getOptions().getLayoutActions());
|
||||
actionGroup.addSeparator();
|
||||
actionGroup.add(PinToolwindowTabAction.getPinAction());
|
||||
if (!isNewLayout) {
|
||||
actionGroup.addSeparator();
|
||||
actionGroup.add(myUi.getOptions().getLayoutActions());
|
||||
actionGroup.addSeparator();
|
||||
actionGroup.add(PinToolwindowTabAction.getPinAction());
|
||||
} else {
|
||||
DefaultActionGroup more = new DefaultActionGroup() {
|
||||
{
|
||||
setPopup(true);
|
||||
getTemplatePresentation().setIcon(AllIcons.Actions.More);
|
||||
getTemplatePresentation().putClientProperty(ActionButton.HIDE_DROPDOWN_ICON, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDumbAware() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
more.add(myUi.getOptions().getLayoutActions());
|
||||
actionGroup.add(more);
|
||||
}
|
||||
return actionGroup;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,24 +4,34 @@ package com.intellij.execution.runners;
|
||||
import com.intellij.diagnostic.logging.LogConsoleManagerBase;
|
||||
import com.intellij.diagnostic.logging.LogFilesManager;
|
||||
import com.intellij.diagnostic.logging.OutputFileUtil;
|
||||
import com.intellij.execution.ExecutionBundle;
|
||||
import com.intellij.execution.configurations.RunConfigurationBase;
|
||||
import com.intellij.execution.configurations.RunProfile;
|
||||
import com.intellij.execution.process.ProcessHandler;
|
||||
import com.intellij.execution.ui.ExecutionConsole;
|
||||
import com.intellij.execution.ui.RunContentDescriptor;
|
||||
import com.intellij.execution.ui.RunnerLayoutUi;
|
||||
import com.intellij.execution.ui.layout.impl.GridImpl;
|
||||
import com.intellij.execution.ui.layout.impl.RunnerContentUi;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.DataProvider;
|
||||
import com.intellij.openapi.actionSystem.LangDataKeys;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.wm.impl.content.SingleContentSupplier;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.GlobalSearchScopes;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.ui.tabs.JBTabs;
|
||||
import com.intellij.ui.tabs.JBTabsEx;
|
||||
import com.intellij.ui.tabs.TabInfo;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class RunTab implements DataProvider, Disposable {
|
||||
@NotNull
|
||||
@@ -71,10 +81,17 @@ public abstract class RunTab implements DataProvider, Disposable {
|
||||
}
|
||||
else if (LangDataKeys.RUN_CONTENT_DESCRIPTOR.is(dataId)) {
|
||||
return myRunContentDescriptor;
|
||||
} else if (SingleContentSupplier.KEY.is(dataId)) {
|
||||
return getSupplier();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected SingleContentSupplier getSupplier() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LogConsoleManagerBase getLogConsoleManager() {
|
||||
if (logConsoleManager == null) {
|
||||
@@ -111,4 +128,80 @@ public abstract class RunTab implements DataProvider, Disposable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link SingleContentSupplier}.
|
||||
*
|
||||
* Isn't used directly by {@link RunTab}, but can be used by inheritors.
|
||||
*/
|
||||
protected class RunTabSupplier implements SingleContentSupplier {
|
||||
|
||||
@Nullable
|
||||
private final ActionGroup myActionGroup;
|
||||
|
||||
public RunTabSupplier(@Nullable ActionGroup group) {
|
||||
myActionGroup = group;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public JBTabs getTabs() {
|
||||
RunnerContentUi contentUi = RunnerContentUi.KEY.getData((DataProvider)myUi);
|
||||
return Objects.requireNonNull(contentUi).getTabs();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ActionGroup getToolbarActions() {
|
||||
return myActionGroup;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<AnAction> getContentActions() {
|
||||
var layout = new ActionGroup(ExecutionBundle.messagePointer("runner.content.tooltip.layout.settings"), () -> "", AllIcons.Debugger.RestoreLayout) {
|
||||
@Override
|
||||
public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) {
|
||||
RunnerContentUi contentUi = RunnerContentUi.KEY.getData((DataProvider)myUi);
|
||||
return Objects.requireNonNull(contentUi).getViewActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
e.getPresentation().setEnabledAndVisible(getChildren(null).length > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDumbAware() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
layout.setPopup(true);
|
||||
layout.getTemplatePresentation().putClientProperty(ActionButton.HIDE_DROPDOWN_ICON, Boolean.TRUE);
|
||||
|
||||
return List.of(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(@Nullable ActionToolbar toolbar) {
|
||||
JBTabs tabs = getTabs();
|
||||
if (tabs instanceof JBTabsEx) {
|
||||
((JBTabsEx)tabs).setHideTopPanel(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
JBTabs tabs = getTabs();
|
||||
if (tabs instanceof JBTabsEx) {
|
||||
((JBTabsEx)tabs).setHideTopPanel(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosable(@NotNull TabInfo tab) {
|
||||
List<Content> gridContents = ((GridImpl)tab.getComponent()).getContents();
|
||||
return gridContents.size() > 0 && gridContents.get(0).isCloseable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +47,10 @@ import com.intellij.ui.docking.DockableContent;
|
||||
import com.intellij.ui.docking.DragSession;
|
||||
import com.intellij.ui.docking.impl.DockManagerImpl;
|
||||
import com.intellij.ui.switcher.QuickActionProvider;
|
||||
import com.intellij.ui.tabs.JBTabPainter;
|
||||
import com.intellij.ui.tabs.JBTabs;
|
||||
import com.intellij.ui.tabs.TabInfo;
|
||||
import com.intellij.ui.tabs.TabsListener;
|
||||
import com.intellij.ui.tabs.impl.DefaultTabPainterAdapter;
|
||||
import com.intellij.ui.tabs.impl.JBTabsImpl;
|
||||
import com.intellij.ui.tabs.impl.TabPainterAdapter;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -251,14 +248,7 @@ public final class RunnerContentUi implements ContentUI, Disposable, CellTransfo
|
||||
private void initUi() {
|
||||
if (myTabs != null) return;
|
||||
|
||||
myTabs = new JBRunnerTabs(myProject, this) {
|
||||
@Override
|
||||
protected TabPainterAdapter createTabPainterAdapter() {
|
||||
return Registry.is("debugger.new.tool.window.layout")
|
||||
? new DefaultTabPainterAdapter(JBTabPainter.getTOOL_WINDOW())
|
||||
: super.createTabPainterAdapter();
|
||||
}
|
||||
};
|
||||
myTabs = new JBRunnerTabs(myProject, this);
|
||||
myTabs.getComponent().setOpaque(false);
|
||||
myTabs.setDataProvider(dataId -> {
|
||||
if (ViewContext.CONTENT_KEY.is(dataId)) {
|
||||
@@ -480,6 +470,13 @@ public final class RunnerContentUi implements ContentUI, Disposable, CellTransfo
|
||||
return myWindow;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JBTabs getTabs() { return myTabs; }
|
||||
|
||||
public AnAction @NotNull[] getViewActions() {
|
||||
return myViewActions.getChildren(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(final @NotNull PropertyChangeEvent evt) {
|
||||
Content content = (Content)evt.getSource();
|
||||
@@ -948,7 +945,7 @@ public final class RunnerContentUi implements ContentUI, Disposable, CellTransfo
|
||||
|
||||
|
||||
NonOpaquePanel sideComponent = new TwoSideComponent(leftWrapper, new TwoSideComponent(middleWrapper, new TwoSideComponent(right, rightWrapper)));
|
||||
|
||||
sideComponent.setVisible(!myTabs.isHideTopPanel());
|
||||
tab.setSideComponent(sideComponent);
|
||||
|
||||
tab.setTabLabelActions((ActionGroup)myActionManager.getAction(VIEW_TOOLBAR), TAB_TOOLBAR_PLACE);
|
||||
|
||||
@@ -41,4 +41,8 @@ public interface JBTabsEx extends JBTabs {
|
||||
void updateTabsLayout(@NotNull TabsLayoutInfo newTabsLayoutInfo);
|
||||
|
||||
void setTitleProducer(@Nullable Producer<Pair<Icon, String>> titleProducer);
|
||||
|
||||
void setHideTopPanel(boolean isHideTopPanel);
|
||||
|
||||
boolean isHideTopPanel();
|
||||
}
|
||||
|
||||
@@ -137,6 +137,7 @@ public class JBTabsImpl extends JComponent
|
||||
private boolean myPaintFocus;
|
||||
|
||||
private boolean myHideTabs;
|
||||
private boolean myHideTopPanel;
|
||||
@Nullable private Project myProject;
|
||||
@NotNull private final Disposable myParentDisposable;
|
||||
|
||||
@@ -1185,7 +1186,7 @@ public class JBTabsImpl extends JComponent
|
||||
return myPopupGroup != null ? myPopupGroup.get() : null;
|
||||
}
|
||||
|
||||
String getPopupPlace() {
|
||||
public String getPopupPlace() {
|
||||
return myPopupPlace;
|
||||
}
|
||||
|
||||
@@ -2516,7 +2517,7 @@ public class JBTabsImpl extends JComponent
|
||||
|
||||
@Override
|
||||
public boolean isHideTabs() {
|
||||
return myHideTabs;
|
||||
return myHideTabs || myHideTopPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2528,6 +2529,27 @@ public class JBTabsImpl extends JComponent
|
||||
relayout(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param hideTopPanel true if tabs and top toolbar should be hidden from a view
|
||||
*/
|
||||
@Override
|
||||
public void setHideTopPanel(boolean hideTopPanel) {
|
||||
if (isHideTopPanel() == hideTopPanel) return;
|
||||
|
||||
myHideTopPanel = hideTopPanel;
|
||||
|
||||
getTabs().stream()
|
||||
.map(TabInfo::getSideComponent)
|
||||
.forEach(component -> component.setVisible(!myHideTopPanel));
|
||||
|
||||
relayout(true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHideTopPanel() {
|
||||
return myHideTopPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JBTabsPresentation setPaintBorder(int top, int left, int right, int bottom) {
|
||||
return this;
|
||||
@@ -2934,6 +2956,10 @@ public class JBTabsImpl extends JComponent
|
||||
return result;
|
||||
}
|
||||
|
||||
public ActionGroup getNavigationActions() {
|
||||
return myNavigationActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataProvider getDataProvider() {
|
||||
return myDataProvider;
|
||||
|
||||
@@ -137,6 +137,13 @@ class ContentTabLabel extends ContentLabel {
|
||||
}
|
||||
}
|
||||
|
||||
protected void closeContent() {
|
||||
ContentManager contentManager = myUi.window.getContentManagerIfCreated();
|
||||
if (contentManager != null) {
|
||||
contentManager.removeContent(myContent, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {
|
||||
setHorizontalAlignment(SwingConstants.LEFT);
|
||||
if (myLayout.isToDrawTabs() == TabContentLayout.TabsDrawMode.HIDE) {
|
||||
@@ -212,10 +219,7 @@ class ContentTabLabel extends ContentLabel {
|
||||
content.setPinned(false);
|
||||
return;
|
||||
}
|
||||
ContentManager contentManager = myUi.window.getContentManagerIfCreated();
|
||||
if (contentManager != null) {
|
||||
contentManager.removeContent(content, true);
|
||||
}
|
||||
closeContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,596 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.wm.impl.content
|
||||
|
||||
import com.intellij.CommonBundle
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.impl.DataManagerImpl
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.ui.JBPopupMenu
|
||||
import com.intellij.openapi.util.BusyObject
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.ui.PopupHandler
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.ui.components.panels.HorizontalLayout
|
||||
import com.intellij.ui.components.panels.NonOpaquePanel
|
||||
import com.intellij.ui.content.AlertIcon
|
||||
import com.intellij.ui.content.Content
|
||||
import com.intellij.ui.content.ContentManager
|
||||
import com.intellij.ui.tabs.*
|
||||
import com.intellij.ui.tabs.impl.JBTabsImpl
|
||||
import com.intellij.ui.tabs.impl.TabLabel
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import com.intellij.util.ui.JBUI
|
||||
import java.awt.*
|
||||
import java.awt.event.ContainerEvent
|
||||
import java.awt.event.ContainerListener
|
||||
import java.beans.PropertyChangeEvent
|
||||
import java.beans.PropertyChangeListener
|
||||
import java.beans.PropertyChangeSupport
|
||||
import javax.swing.Icon
|
||||
import javax.swing.JComponent
|
||||
|
||||
/**
|
||||
* Tool window header that shows tabs and actions from its content.
|
||||
*
|
||||
* If toolwindow [Content] returns [SingleContentSupplier] as a data provider
|
||||
* via [SingleContentSupplier.KEY] that in case of single content view
|
||||
* all [JBTabs] and actions are moved into this header.
|
||||
*
|
||||
* When two or more contents exist then header looks like [TabContentLayout].
|
||||
*/
|
||||
internal class SingleContentLayout(
|
||||
ui: ToolWindowContentUi
|
||||
) : TabContentLayout(ui) {
|
||||
|
||||
private var isSingleContentView: Boolean = false
|
||||
|
||||
private var tabAdapter: TabAdapter? = null
|
||||
private val toolbars = mutableMapOf<ActionToolbarPosition, ActionToolbar>()
|
||||
private val wrapper = NonOpaquePanel(HorizontalLayout(0))
|
||||
|
||||
override fun update() {
|
||||
super.update()
|
||||
tryUpdateContentView()
|
||||
}
|
||||
|
||||
override fun rebuild() {
|
||||
super.rebuild()
|
||||
tryUpdateContentView()
|
||||
}
|
||||
|
||||
private fun Content.getSupplier(): SingleContentSupplier? {
|
||||
return (component as? DataProvider)?.let(SingleContentSupplier.KEY::getData)
|
||||
}
|
||||
|
||||
private fun getSingleContentOrNull(): Content? {
|
||||
return if (myTabs.size == 1) myTabs[0].content else null
|
||||
}
|
||||
|
||||
private fun tryUpdateContentView() {
|
||||
val currentContent = getSingleContentOrNull()
|
||||
val contentSupplier = currentContent?.getSupplier()
|
||||
|
||||
if (contentSupplier != null) {
|
||||
if (isSingleContentView) {
|
||||
updateSingleContentView(currentContent, contentSupplier)
|
||||
}
|
||||
else {
|
||||
initSingleContentView(currentContent, contentSupplier)
|
||||
}
|
||||
}
|
||||
else if (isSingleContentView) {
|
||||
resetSingleContentView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSingleContentView(content: Content, supplier: SingleContentSupplier) {
|
||||
tabAdapter = TabAdapter(content, supplier.getTabs(), tabPainter, myUi).also {
|
||||
Disposer.register(content, it)
|
||||
myUi.tabComponent.add(it)
|
||||
}
|
||||
assert(toolbars.isEmpty())
|
||||
supplier.getToolbarActions()?.let { mainActionGroup ->
|
||||
toolbars[ActionToolbarPosition.LEFT] = createToolbar(
|
||||
"MainSingleContentToolbar",
|
||||
mainActionGroup,
|
||||
content.component
|
||||
)
|
||||
}
|
||||
|
||||
let {
|
||||
toolbars[ActionToolbarPosition.RIGHT] = createToolbar(
|
||||
"CloseSingleContentGroup",
|
||||
DefaultActionGroup(emptyList<AnAction>() + CloseCurrentContentAction() + supplier.getContentActions()),
|
||||
content.component
|
||||
).apply {
|
||||
setReservePlaceAutoPopupIcon(false)
|
||||
}
|
||||
}
|
||||
|
||||
toolbars.forEach { (_, toolbar) -> myUi.tabComponent.add(toolbar.component) }
|
||||
|
||||
wrapper.removeAll()
|
||||
ToolWindowContentUi.initMouseListeners(wrapper, myUi, true)
|
||||
myUi.tabComponent.add(wrapper)
|
||||
|
||||
isSingleContentView = true
|
||||
supplier.init(toolbars[ActionToolbarPosition.LEFT])
|
||||
supplier.customize(wrapper)
|
||||
}
|
||||
|
||||
private fun updateSingleContentView(content: Content, supplier: SingleContentSupplier) {
|
||||
if (tabAdapter?.jbTabs != supplier.getTabs()) {
|
||||
// in case of 'reusing content' just revoke old view and create new one
|
||||
resetSingleContentView()
|
||||
initSingleContentView(content, supplier)
|
||||
}
|
||||
else {
|
||||
toolbars.forEach { (_, toolbar) ->
|
||||
toolbar.updateActionsImmediately()
|
||||
}
|
||||
supplier.customize(wrapper)
|
||||
myUi.tabComponent.repaint()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetSingleContentView() {
|
||||
val adapter = tabAdapter ?: error("Adapter must not be null")
|
||||
tabAdapter = null
|
||||
myUi.tabComponent.remove(adapter)
|
||||
Disposer.dispose(adapter)
|
||||
|
||||
toolbars.values.forEach {
|
||||
myUi.tabComponent.remove(it.component)
|
||||
}
|
||||
toolbars.clear()
|
||||
|
||||
myUi.tabComponent.remove(wrapper)
|
||||
wrapper.removeAll()
|
||||
|
||||
isSingleContentView = false
|
||||
adapter.content.getSupplier()?.reset()
|
||||
}
|
||||
|
||||
private fun createToolbar(place: String, group: ActionGroup, target: JComponent? = null): ActionToolbar {
|
||||
val toolbar = ActionManager.getInstance().createActionToolbar(place, group, true)
|
||||
toolbar.setTargetComponent(target)
|
||||
toolbar.component.isOpaque = false
|
||||
return toolbar
|
||||
}
|
||||
|
||||
override fun isToDrawTabs(): TabsDrawMode {
|
||||
return if (isSingleContentView) TabsDrawMode.HIDE else super.isToDrawTabs()
|
||||
}
|
||||
|
||||
override fun layout() {
|
||||
super.layout()
|
||||
|
||||
if (isSingleContentView) {
|
||||
val component = myUi.tabComponent
|
||||
component.bounds = component.bounds.apply { width = component.parent.width }
|
||||
|
||||
var toolbarWidth = myIdLabel.x + myIdLabel.width
|
||||
|
||||
tabAdapter?.apply {
|
||||
bounds = Rectangle(toolbarWidth, 0, preferredSize.width, component.height)
|
||||
toolbarWidth += bounds.width
|
||||
}
|
||||
|
||||
val rightToolbarWidth = toolbars[ActionToolbarPosition.RIGHT]?.component?.run {
|
||||
bounds = Rectangle(component.width - preferredSize.width, 0, preferredSize.width, component.height)
|
||||
bounds.width
|
||||
} ?: 0
|
||||
|
||||
toolbars[ActionToolbarPosition.LEFT]?.component?.apply {
|
||||
bounds = Rectangle(
|
||||
toolbarWidth,
|
||||
0,
|
||||
minOf(preferredSize.width, component.width - rightToolbarWidth - toolbarWidth),
|
||||
component.height
|
||||
)
|
||||
toolbarWidth += bounds.width
|
||||
}
|
||||
|
||||
wrapper.bounds = Rectangle(toolbarWidth, 0, maxOf(component.width - rightToolbarWidth - toolbarWidth, 0), component.height)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateIdLabel(label: BaseLabel) {
|
||||
if (!isSingleContentView) {
|
||||
label.icon = null
|
||||
super.updateIdLabel(label)
|
||||
}
|
||||
else if (myTabs.size == 1) {
|
||||
label.icon = myTabs[0].content.icon
|
||||
label.text = myTabs[0].content.displayName
|
||||
label.border = JBUI.Borders.empty(0, 2, 0, 7)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class TabAdapter(
|
||||
val content: Content,
|
||||
val jbTabs: JBTabs,
|
||||
val tabPainter: JBTabPainter,
|
||||
val twcui: ToolWindowContentUi
|
||||
) : NonOpaquePanel(HorizontalLayout(0)), TabsListener, PropertyChangeListener, Disposable {
|
||||
|
||||
val labels = mutableListOf<MyContentTabLabel>()
|
||||
|
||||
val popupHandler = object : PopupHandler() {
|
||||
override fun invokePopup(comp: Component, x: Int, y: Int) = showPopup(comp, x, y)
|
||||
}
|
||||
|
||||
val containerListener = object : ContainerListener {
|
||||
override fun componentAdded(e: ContainerEvent) = update(e)
|
||||
override fun componentRemoved(e: ContainerEvent) = update(e)
|
||||
private fun update(e: ContainerEvent) {
|
||||
if (e.child is TabLabel) {
|
||||
checkAndUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
updateTabs()
|
||||
|
||||
jbTabs.addListener(object : TabsListener {
|
||||
override fun selectionChanged(oldSelection: TabInfo?, newSelection: TabInfo?) = checkAndUpdate()
|
||||
override fun tabRemoved(tabToRemove: TabInfo) = checkAndUpdate()
|
||||
override fun tabsMoved() = checkAndUpdate()
|
||||
}, this)
|
||||
|
||||
jbTabs.component.addContainerListener(containerListener)
|
||||
}
|
||||
|
||||
private fun retrieveInfo(label: MyContentTabLabel): TabInfo {
|
||||
return label.content.info
|
||||
}
|
||||
|
||||
private fun checkAndUpdate() {
|
||||
val currentContent = getSingleContentOrNull()
|
||||
val currentSupplier = currentContent?.getSupplier()
|
||||
val src = currentSupplier?.getTabs()?.tabs ?: return
|
||||
val dst = labels.map(::retrieveInfo)
|
||||
if (!ContainerUtil.equalsIdentity(src, dst)) {
|
||||
updateTabs()
|
||||
}
|
||||
updateSingleContentView(currentContent, currentSupplier)
|
||||
updateLabels(labels)
|
||||
}
|
||||
|
||||
fun updateTabs() {
|
||||
labels.removeAll {
|
||||
retrieveInfo(it).changeSupport.removePropertyChangeListener(this)
|
||||
remove(it)
|
||||
true
|
||||
}
|
||||
|
||||
val supplier = getSingleContentOrNull()?.getSupplier() ?: return
|
||||
if (jbTabs.tabs.size > 1) {
|
||||
labels.addAll(jbTabs.tabs.map { info ->
|
||||
info.changeSupport.addPropertyChangeListener(this)
|
||||
MyContentTabLabel(FakeContent(supplier, info), this@SingleContentLayout)
|
||||
})
|
||||
labels.forEach(::add)
|
||||
}
|
||||
updateLabels(labels)
|
||||
}
|
||||
|
||||
fun updateLabels(labels: List<MyContentTabLabel>) {
|
||||
labels.associateBy { it.content.info }.forEach(::copyValues)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
labels.forEach {
|
||||
retrieveInfo(it).changeSupport.removePropertyChangeListener(this)
|
||||
}
|
||||
jbTabs.component.removeContainerListener(containerListener)
|
||||
}
|
||||
|
||||
fun copyValues(from: TabInfo, to: ContentLabel) {
|
||||
to.icon = from.icon
|
||||
to.text = from.text
|
||||
}
|
||||
|
||||
fun showPopup(component: Component, x: Int, y: Int) {
|
||||
// ViewContext.CONTENT_KEY
|
||||
val info = ((component as? ContentTabLabel)?.content as? FakeContent)?.info
|
||||
if (info == null) {
|
||||
logger<SingleContentLayout>().warn("Cannot resolve label popup component. Event will be ignored")
|
||||
return
|
||||
}
|
||||
val supplier = getSingleContentOrNull()?.getSupplier()
|
||||
val toShow = getPopupMenu(supplier?.getTabs()) ?: return
|
||||
toShow.setTargetComponent(component)
|
||||
JBPopupMenu.showAt(RelativePoint(component, Point(x, y)), toShow.component)
|
||||
}
|
||||
|
||||
private fun getPopupMenu(tabs: JBTabs?): ActionPopupMenu? {
|
||||
val jbTabsImpl = tabs as? JBTabsImpl ?: return null
|
||||
val popup = jbTabsImpl.popupGroup ?: return null
|
||||
val popupPlace = jbTabsImpl.popupPlace ?: ActionPlaces.UNKNOWN
|
||||
|
||||
val group = DefaultActionGroup()
|
||||
group.addAll(popup)
|
||||
group.addSeparator()
|
||||
group.addAll(jbTabsImpl.navigationActions)
|
||||
|
||||
return ActionManager.getInstance().createActionPopupMenu(popupPlace, group)
|
||||
}
|
||||
|
||||
override fun paintComponent(g: Graphics) {
|
||||
if (g is Graphics2D) {
|
||||
labels.forEach { comp ->
|
||||
val labelBounds = comp.bounds
|
||||
if (jbTabs.selectedInfo == retrieveInfo(comp)) {
|
||||
tabPainter.paintSelectedTab(JBTabsPosition.top, g, labelBounds, 1, null, twcui.window.isActive, comp.isHovered)
|
||||
}
|
||||
else {
|
||||
tabPainter.paintTab(JBTabsPosition.top, g, labelBounds, 1, null, twcui.window.isActive, comp.isHovered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.paintComponent(g)
|
||||
}
|
||||
|
||||
override fun propertyChange(evt: PropertyChangeEvent) {
|
||||
val source = evt.source as? TabInfo ?: error("Bad event source")
|
||||
val label = labels.find { it.content.info == source } ?: error("Cannot find label for $source")
|
||||
copyValues(source, label)
|
||||
}
|
||||
|
||||
private inner class MyContentTabLabel(content: FakeContent, layout: TabContentLayout) : ContentTabLabel(content, layout), DataProvider {
|
||||
|
||||
init {
|
||||
addMouseListener(popupHandler)
|
||||
}
|
||||
|
||||
override fun selectContent() {
|
||||
retrieveInfo(this).let { jbTabs.select(it, true) }
|
||||
}
|
||||
|
||||
override fun closeContent() {
|
||||
retrieveInfo(this).let(jbTabs::removeTab)
|
||||
}
|
||||
|
||||
override fun getData(dataId: String): Any? {
|
||||
if (JBTabsEx.NAVIGATION_ACTIONS_KEY.`is`(dataId)) {
|
||||
return jbTabs
|
||||
}
|
||||
return DataManagerImpl.getDataProviderEx(retrieveInfo(this).component)?.getData(dataId)
|
||||
}
|
||||
|
||||
override fun getContent(): FakeContent {
|
||||
return super.getContent() as FakeContent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class CloseCurrentContentAction : DumbAwareAction(CommonBundle.messagePointer("action.close"), AllIcons.Actions.Cancel) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
getSingleContentOrNull()?.let { it.manager?.removeContent(it, true) }
|
||||
}
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
e.presentation.isEnabledAndVisible = getSingleContentOrNull()?.isCloseable == true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimal [Content] implementation.
|
||||
*
|
||||
* Is used only to pass as an argument to support [SingleContentLayout.TabAdapter.MyContentTabLabel].
|
||||
*
|
||||
* All unused methods throw [IllegalStateException].
|
||||
*/
|
||||
private class FakeContent(val supplier: SingleContentSupplier, val info: TabInfo) : Content {
|
||||
|
||||
private val pcs = PropertyChangeSupport(this)
|
||||
|
||||
override fun addPropertyChangeListener(l: PropertyChangeListener?) {
|
||||
pcs.addPropertyChangeListener(l)
|
||||
}
|
||||
|
||||
override fun removePropertyChangeListener(l: PropertyChangeListener?) {
|
||||
pcs.removePropertyChangeListener(l)
|
||||
}
|
||||
|
||||
override fun isSelected(): Boolean {
|
||||
return supplier.getTabs().selectedInfo == info
|
||||
}
|
||||
|
||||
override fun isCloseable(): Boolean {
|
||||
return supplier.isClosable(info)
|
||||
}
|
||||
|
||||
override fun isPinned(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getManager(): ContentManager? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun <T : Any?> getUserData(key: Key<T>): T? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun <T : Any?> putUserData(key: Key<T>, value: T?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getComponent(): JComponent {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getPreferredFocusableComponent(): JComponent? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setComponent(component: JComponent?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setPreferredFocusableComponent(component: JComponent?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setPreferredFocusedComponent(computable: Computable<out JComponent>?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setIcon(icon: Icon?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getIcon(): Icon? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setDisplayName(displayName: String?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getDisplayName(): String? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setTabName(tabName: String?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getTabName(): String {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getToolwindowTitle(): String {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setToolwindowTitle(toolwindowTitle: String?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getDisposer(): Disposable? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setDisposer(disposer: Disposable) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setShouldDisposeContent(value: Boolean) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getDescription(): String? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setDescription(description: String?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setPinned(locked: Boolean) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setPinnable(pinnable: Boolean) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun isPinnable(): Boolean {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setCloseable(closeable: Boolean) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setActions(actions: ActionGroup?, place: String?, contextComponent: JComponent?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getActions(): ActionGroup? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setSearchComponent(comp: JComponent?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getSearchComponent(): JComponent? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getPlace(): String {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getActionsContextComponent(): JComponent? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setAlertIcon(icon: AlertIcon?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getAlertIcon(): AlertIcon? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun fireAlert() {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getBusyObject(): BusyObject? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setBusyObject(`object`: BusyObject?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getSeparator(): String {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setSeparator(separator: String?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setPopupIcon(icon: Icon?) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getPopupIcon(): Icon? {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun setExecutionId(executionId: Long) {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
|
||||
override fun getExecutionId(): Long {
|
||||
error("An operation is not supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.wm.impl.content
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionGroup
|
||||
import com.intellij.openapi.actionSystem.ActionToolbar
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.DataKey
|
||||
import com.intellij.ui.tabs.JBTabs
|
||||
import com.intellij.ui.tabs.TabInfo
|
||||
import javax.swing.JComponent
|
||||
|
||||
/**
|
||||
* Describes tabs and toolbar for the [SingleContentLayout].
|
||||
*/
|
||||
interface SingleContentSupplier {
|
||||
|
||||
/**
|
||||
* Tabs will be copied into toolwindow header and managed by [SingleContentLayout].
|
||||
*/
|
||||
fun getTabs() : JBTabs
|
||||
|
||||
/**
|
||||
* Toolbar follows after the tabs.
|
||||
*
|
||||
* By default, current toolwindow content is used for [ActionToolbar.setTargetComponent].
|
||||
* Toolbars can be adjusted in [init].
|
||||
*/
|
||||
@JvmDefault
|
||||
fun getToolbarActions() : ActionGroup? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions after close action.
|
||||
*/
|
||||
@JvmDefault
|
||||
fun getContentActions() : List<AnAction> {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if a tab from [getTabs] can be closed.
|
||||
*/
|
||||
@JvmDefault
|
||||
fun isClosable(tab: TabInfo) : Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after a single view mode is activated.
|
||||
*
|
||||
* @param toolbar main toolbar that can be customized, e.g. [ActionToolbar.setTargetComponent]
|
||||
*/
|
||||
fun init(toolbar: ActionToolbar?) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to customize wrappers after tabs are changed or new view is set.
|
||||
*
|
||||
* @param wrapper additional empty panel between toolbar and close action where something can be put
|
||||
*/
|
||||
@JvmDefault
|
||||
fun customize(wrapper: JComponent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called after a single view mode was revoked.
|
||||
*/
|
||||
@JvmDefault
|
||||
fun reset() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val KEY = DataKey.create<SingleContentSupplier>("ToolbarContentSupplier")
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
final class TabContentLayout extends ContentLayout implements MorePopupAware {
|
||||
class TabContentLayout extends ContentLayout implements MorePopupAware {
|
||||
static final int MORE_ICON_BORDER = 6;
|
||||
public static final int TAB_LAYOUT_START = 4;
|
||||
LayoutData myLastLayout;
|
||||
@@ -288,7 +288,7 @@ final class TabContentLayout extends ContentLayout implements MorePopupAware {
|
||||
}
|
||||
}
|
||||
|
||||
private final JBTabPainter tabPainter = JBTabPainter.getTOOL_WINDOW();
|
||||
protected final JBTabPainter tabPainter = JBTabPainter.getTOOL_WINDOW();
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) {
|
||||
|
||||
@@ -86,7 +86,7 @@ public final class ToolWindowContentUi implements ContentUI, DataProvider {
|
||||
@NotNull JPanel contentComponent) {
|
||||
this.contentManager = contentManager;
|
||||
type = window.getWindowInfo().getContentUiType();
|
||||
tabsLayout = new TabContentLayout(this);
|
||||
tabsLayout = new SingleContentLayout(this);
|
||||
this.window = window;
|
||||
this.contentComponent = contentComponent;
|
||||
|
||||
|
||||
@@ -3,33 +3,22 @@ package com.intellij.xdebugger.impl.ui
|
||||
|
||||
import com.intellij.debugger.ui.DebuggerContentInfo
|
||||
import com.intellij.execution.runners.ExecutionEnvironment
|
||||
import com.intellij.execution.runners.ExecutionUtil
|
||||
import com.intellij.execution.ui.layout.LayoutAttractionPolicy
|
||||
import com.intellij.execution.ui.layout.PlaceInGrid
|
||||
import com.intellij.execution.ui.layout.impl.RunnerLayoutUiImpl
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.actions.TabListAction
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.ui.PersistentThreeComponentSplitter
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.Pair.create
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.wm.ToolWindowId
|
||||
import com.intellij.openapi.wm.ToolWindowManager
|
||||
import com.intellij.openapi.wm.ToolWindowType
|
||||
import com.intellij.openapi.wm.ex.ToolWindowEx
|
||||
import com.intellij.openapi.wm.ex.ToolWindowManagerListener
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.content.ContentManagerEvent
|
||||
import com.intellij.ui.content.ContentManagerListener
|
||||
import com.intellij.util.Producer
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.openapi.wm.impl.content.SingleContentSupplier
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.xdebugger.*
|
||||
import com.intellij.xdebugger.XDebuggerBundle
|
||||
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
||||
import com.intellij.xdebugger.impl.actions.XDebuggerActions
|
||||
import com.intellij.xdebugger.impl.frame.*
|
||||
@@ -39,8 +28,6 @@ import java.awt.Component
|
||||
import java.awt.Container
|
||||
import javax.swing.Icon
|
||||
import javax.swing.LayoutFocusTraversalPolicy
|
||||
import javax.swing.event.AncestorEvent
|
||||
import javax.swing.event.AncestorListener
|
||||
|
||||
//TODO: unify with XDebugSessionTab2
|
||||
class XDebugSessionTab3(
|
||||
@@ -70,86 +57,7 @@ class XDebugSessionTab3(
|
||||
|
||||
private val focusTraversalPolicy = MyFocusTraversalPolicy()
|
||||
|
||||
init {
|
||||
// value from com.intellij.execution.ui.layout.impl.GridImpl
|
||||
splitter.setMinSize(48)
|
||||
|
||||
splitter.isFocusCycleRoot = true
|
||||
splitter.isFocusTraversalPolicyProvider = true
|
||||
splitter.focusTraversalPolicy = focusTraversalPolicy
|
||||
|
||||
session.addSessionListener(object : XDebugSessionListener {
|
||||
override fun sessionStopped() {
|
||||
UIUtil.invokeLaterIfNeeded {
|
||||
splitter.saveProportions()
|
||||
Disposer.dispose(lifetime)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
project.messageBus.connect(lifetime).subscribe(XDebuggerManager.TOPIC, object : XDebuggerManagerListener {
|
||||
override fun processStarted(debugProcess: XDebugProcess) {
|
||||
UIUtil.invokeLaterIfNeeded {
|
||||
if (debugProcess.session != null && debugProcess.session != session) {
|
||||
splitter.saveProportions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun currentSessionChanged(previousSession: XDebugSession?, currentSession: XDebugSession?) {
|
||||
UIUtil.invokeLaterIfNeeded {
|
||||
if (previousSession == session) {
|
||||
splitter.saveProportions()
|
||||
//xThreadsFramesView.saveUiState()
|
||||
}
|
||||
else if (currentSession == session)
|
||||
splitter.restoreProportions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun processStopped(debugProcess: XDebugProcess) {
|
||||
UIUtil.invokeLaterIfNeeded {
|
||||
splitter.saveProportions()
|
||||
//xThreadsFramesView.saveUiState()
|
||||
if (debugProcess.session == session)
|
||||
Disposer.dispose(lifetime)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
val ancestorListener = object : AncestorListener {
|
||||
override fun ancestorAdded(event: AncestorEvent?) {
|
||||
if (XDebuggerManager.getInstance(project).currentSession == session) {
|
||||
splitter.restoreProportions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun ancestorRemoved(event: AncestorEvent?) {
|
||||
if (XDebuggerManager.getInstance(project).currentSession == session) {
|
||||
splitter.saveProportions()
|
||||
//xThreadsFramesView.saveUiState()
|
||||
}
|
||||
}
|
||||
|
||||
override fun ancestorMoved(event: AncestorEvent?) {
|
||||
}
|
||||
}
|
||||
|
||||
toolWindow?.component?.addAncestorListener(ancestorListener)
|
||||
Disposer.register(lifetime, Disposable {
|
||||
toolWindow?.component?.removeAncestorListener(ancestorListener)
|
||||
})
|
||||
|
||||
var oldToolWindowType: ToolWindowType? = null
|
||||
project.messageBus.connect(lifetime).subscribe(ToolWindowManagerListener.TOPIC, object : ToolWindowManagerListener {
|
||||
override fun stateChanged(toolWindowManager: ToolWindowManager) {
|
||||
if (oldToolWindowType == toolWindow?.type) return
|
||||
|
||||
setHeaderState()
|
||||
oldToolWindowType = toolWindow?.type
|
||||
}
|
||||
})
|
||||
}
|
||||
private var mySingleContentSupplier: SingleContentSupplier? = null
|
||||
|
||||
override fun getWatchesContentId() = debuggerContentId
|
||||
override fun getFramesContentId() = debuggerContentId
|
||||
@@ -207,25 +115,6 @@ class XDebugSessionTab3(
|
||||
myUi.addContent(content, 0, PlaceInGrid.left, false)
|
||||
|
||||
ui.defaults.initContentAttraction(debuggerContentId, XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION, LayoutAttractionPolicy.FocusOnce())
|
||||
|
||||
toolWindow?.let {
|
||||
val contentManager = it.contentManager
|
||||
val listener = object : ContentManagerListener {
|
||||
override fun contentAdded(event: ContentManagerEvent) {
|
||||
setHeaderState()
|
||||
}
|
||||
|
||||
override fun contentRemoved(event: ContentManagerEvent) {
|
||||
setHeaderState()
|
||||
}
|
||||
}
|
||||
contentManager.addContentManagerListener(listener)
|
||||
Disposer.register(lifetime, Disposable {
|
||||
contentManager.removeContentManagerListener(listener)
|
||||
})
|
||||
}
|
||||
|
||||
setHeaderState()
|
||||
}
|
||||
private fun getComponents(): Iterator<Component> {
|
||||
return iterator {
|
||||
@@ -261,52 +150,14 @@ class XDebugSessionTab3(
|
||||
|
||||
myUi.options.setTopLeftToolbar(toolbar, ActionPlaces.DEBUGGER_TOOLBAR)
|
||||
|
||||
myUi.options.setTitleProducer(Producer {
|
||||
val icon = if (session.isStopped) session.runProfile?.icon else ExecutionUtil.getLiveIndicator(session.runProfile?.icon)
|
||||
return@Producer create(icon, StringUtil.shortenTextWithEllipsis(session.sessionName, 15, 0) + ":")
|
||||
})
|
||||
mySingleContentSupplier = RunTabSupplier(toolbar)
|
||||
|
||||
val settings = DefaultActionGroup(*myUi.options.settingsActionsList)
|
||||
registerAdditionalActions(DefaultActionGroup(), DefaultActionGroup(), settings)
|
||||
(toolWindow as ToolWindowEx).setAdditionalGearActions(settings)
|
||||
}
|
||||
|
||||
private fun setHeaderState() {
|
||||
toolWindow?.let { toolWindow ->
|
||||
if (toolWindow !is ToolWindowEx) return@let
|
||||
|
||||
val singleContent = toolWindow.contentManager.contents.singleOrNull()
|
||||
val headerVisible = toolWindow.isHeaderVisible
|
||||
val topRightToolbar = DefaultActionGroup().apply {
|
||||
if (headerVisible) return@apply
|
||||
addAll(toolWindow.decorator.headerToolbar.actions.filter { it != null && it !is TabListAction })
|
||||
}
|
||||
myUi.options.setTopRightToolbar(topRightToolbar, ActionPlaces.DEBUGGER_TOOLBAR)
|
||||
|
||||
val topMiddleToolbar = DefaultActionGroup().apply {
|
||||
if (singleContent == null || headerVisible) return@apply
|
||||
|
||||
add(object : AnAction(XDebuggerBundle.message("session.tab.close.debug.session"), null, AllIcons.Actions.Cancel) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
toolWindow.contentManager.removeContent(singleContent, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
myUi.options.setTopMiddleToolbar(topMiddleToolbar, ActionPlaces.DEBUGGER_TOOLBAR)
|
||||
|
||||
toolWindow.decorator.isHeaderVisible = headerVisible
|
||||
|
||||
if (toolWindow.decorator.isHeaderVisible) {
|
||||
toolWindow.component.border = null
|
||||
toolWindow.component.invalidate()
|
||||
toolWindow.component.repaint()
|
||||
} else if (toolWindow.component.border == null) {
|
||||
UIUtil.addBorder(toolWindow.component, JBUI.Borders.customLine(JBColor.border(), 1, 0, 0, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val ToolWindowEx.isHeaderVisible get() = (type != ToolWindowType.DOCKED) || contentManager.contents.singleOrNull() == null
|
||||
override fun getSupplier(): SingleContentSupplier? = mySingleContentSupplier
|
||||
|
||||
override fun registerAdditionalActions(leftToolbar: DefaultActionGroup, topLeftToolbar: DefaultActionGroup, settings: DefaultActionGroup) {
|
||||
leftToolbar.apply {
|
||||
|
||||
Reference in New Issue
Block a user