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 62864d01b91a..e6311fbae473 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java @@ -37,6 +37,8 @@ import java.awt.event.MouseEvent; * @author Vladimir Kondratyev */ public class ThreeComponentsSplitter extends JPanel implements Disposable { + public static final int MIN_SIZE = 48; + private int myDividerWidth; /** * /------/ @@ -189,14 +191,22 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { else { firstCompontSize = getFirstSize(); lastComponentSize = getLastSize(); - int sizeLack = firstCompontSize + lastComponentSize - (componentSize - dividersCount * dividerWidth); + int sizeLack = firstCompontSize + lastComponentSize - (componentSize - dividersCount * dividerWidth - MIN_SIZE); if (sizeLack > 0) { - // Lacking size. Reduce first component's size, inner -> empty - firstCompontSize -= sizeLack; - innerComponentSize = 0; + // Lacking size. Reduce first & last component's size, inner -> MIN_SIZE + double firstSizeRatio = (double)firstCompontSize / (firstCompontSize + lastComponentSize); + if (firstCompontSize > 0) { + firstCompontSize -= sizeLack * firstSizeRatio; + firstCompontSize = Math.max(MIN_SIZE, firstCompontSize); + } + if (lastComponentSize > 0) { + lastComponentSize -= sizeLack * (1 - firstSizeRatio); + lastComponentSize = Math.max(MIN_SIZE, lastComponentSize); + } + innerComponentSize = MIN_SIZE; } else { - innerComponentSize = componentSize - dividersCount * dividerWidth - getFirstSize() - getLastSize(); + innerComponentSize = Math.max(MIN_SIZE, componentSize - dividersCount * dividerWidth - getFirstSize() - getLastSize()); } if (!innerVisible()) { @@ -395,6 +405,29 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { return lastVisible() ? myLastSize : 0; } + public int getMinSize(boolean first) { + return getMinSize(first? myFirstComponent : myLastComponent); + } + + public int getMaxSize(boolean first) { + final int size = getOrientation() ? this.getHeight() : this.getWidth(); + return size - (first? myLastSize: myFirstSize) - MIN_SIZE; + } + + private int getMinSize(JComponent component) { + if (isHonorMinimumSize()) { + if (component != null && myFirstComponent != null && myFirstComponent.isVisible() && myLastComponent != null && myLastComponent.isVisible()) { + if (getOrientation()) { + return component.getMinimumSize().height; + } + else { + return component.getMinimumSize().width; + } + } + } + return MIN_SIZE; + } + @Override public void dispose() { myLastComponent = null; @@ -633,23 +666,24 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { myGlassPane.setCursor(getResizeCursor(), myListener); myPoint = SwingUtilities.convertPoint(this, e.getPoint(), ThreeComponentsSplitter.this); + final int size = getOrientation() ? ThreeComponentsSplitter.this.getHeight() : ThreeComponentsSplitter.this.getWidth(); if (getOrientation()) { - if (getHeight() > 0 || myDividerZone > 0) { + if (size > 0 || myDividerZone > 0) { if (myIsFirst) { - setFirstSize(Math.max(getMinSize(myFirstComponent), myPoint.y)); + setFirstSize(Math.min(size - myLastSize - MIN_SIZE, Math.max(getMinSize(myFirstComponent), myPoint.y))); } else { - setLastSize(Math.max(getMinSize(myLastComponent), ThreeComponentsSplitter.this.getHeight() - myPoint.y - getDividerWidth())); + setLastSize(Math.min(size - myFirstSize - MIN_SIZE, Math.max(getMinSize(myLastComponent), size - myPoint.y - getDividerWidth()))); } } } else { - if (getWidth() > 0 || myDividerZone > 0) { + if (size > 0 || myDividerZone > 0) { if (myIsFirst) { - setFirstSize(Math.max(getMinSize(myFirstComponent), myPoint.x)); + setFirstSize(Math.min(size - myLastSize - MIN_SIZE, Math.max(getMinSize(myFirstComponent), myPoint.x))); } else { - setLastSize(Math.max(getMinSize(myLastComponent), ThreeComponentsSplitter.this.getWidth() - myPoint.x - getDividerWidth())); + setLastSize(Math.min(size - myFirstSize - MIN_SIZE, Math.max(getMinSize(myLastComponent), size - myPoint.x - getDividerWidth()))); } } } @@ -670,20 +704,6 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable { } } - private int getMinSize(JComponent component) { - if (isHonorMinimumSize()) { - if (component != null && myFirstComponent != null && myFirstComponent.isVisible() && myLastComponent != null && myLastComponent.isVisible()) { - if (getOrientation()) { - return component.getMinimumSize().height; - } - else { - return component.getMinimumSize().width; - } - } - } - return 0; - } - protected void processMouseEvent(MouseEvent e) { super.processMouseEvent(e); if (!isShowing()) { diff --git a/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java b/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java index b616429a11c8..654f43e0c2ec 100644 --- a/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java +++ b/platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java @@ -154,4 +154,8 @@ public abstract class ToolWindowManager { @Nullable public abstract Balloon getToolWindowBalloon(String id); + + public abstract boolean isMaximized(@NotNull ToolWindow wnd); + + public abstract void setMaximized(@NotNull ToolWindow wnd, boolean maximized); } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java index bf9a015bdfeb..371d847f1699 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java @@ -402,6 +402,7 @@ public final class InternalDecorator extends JPanel implements Queryable, DataPr resize.add(new ResizeToolWindowAction.Right(myToolWindow, this)); resize.add(new ResizeToolWindowAction.Up(myToolWindow, this)); resize.add(new ResizeToolWindowAction.Down(myToolWindow, this)); + resize.add(ActionManager.getInstance().getAction("MaximizeToolWindow")); group.add(resize); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java new file mode 100644 index 000000000000..29d68ad59080 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2014 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.wm.impl; + +import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import org.jetbrains.annotations.NotNull; + +public class MaximizeToolWindowAction extends AnAction { + public MaximizeToolWindowAction() { + super(); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + Project project = e.getProject(); + if (project == null || project.isDisposed()) return; + ToolWindow toolWindow = e.getData(PlatformDataKeys.TOOL_WINDOW); + if (toolWindow == null) return; + ToolWindowManager manager = ToolWindowManager.getInstance(project); + manager.setMaximized(toolWindow, !manager.isMaximized(toolWindow)); + } + + @Override + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabled(true); + Project project = e.getProject(); + if (project == null || project.isDisposed()) { + e.getPresentation().setEnabled(false); + return; + } + ToolWindow toolWindow = e.getData(PlatformDataKeys.TOOL_WINDOW); + if (toolWindow == null) { + e.getPresentation().setEnabled(false); + return; + } + ToolWindowManager manager = ToolWindowManager.getInstance(project); + e.getPresentation().setText(manager.isMaximized(toolWindow) ? + ActionsBundle.message("action.ResizeToolWindowMaximize.text.alternative") : + ActionsBundle.message("action.ResizeToolWindowMaximize.text")); + } +} 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 101d3ab2762b..7d64b1180e15 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 @@ -25,9 +25,11 @@ import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.impl.*; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; +import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.ToolWindowType; import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; import com.intellij.ui.InplaceButton; @@ -210,6 +212,12 @@ public abstract class ToolWindowHeader extends JPanel implements Disposable, UIS }; } + void switchMaximizedState(Project project) { + if (project == null || project.isDisposed()) return; + ToolWindowManager mgr = ToolWindowManager.getInstance(project); + mgr.setMaximized(myToolWindow, !mgr.isMaximized(myToolWindow)); + } + @Override public void uiSettingsChanged(UISettings source) { clearCaches(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java index 7b9a7494c640..5fa360235cb2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java @@ -198,6 +198,15 @@ public class ToolWindowHeadlessManagerImpl extends ToolWindowManagerEx { return null; } + @Override + public boolean isMaximized(@NotNull ToolWindow wnd) { + return false; + } + + @Override + public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) { + } + @Override public void initToolWindow(@NotNull ToolWindowEP bean) { diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java index c0c27ba8c128..8acebb5b5bfd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java @@ -120,6 +120,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements private final FileEditorManager myFileEditorManager; private final LafManager myLafManager; private final Map myWindow2Balloon = new HashMap(); + private Pair myMaximizedToolwindowSize = null; private KeyState myCurrentState = KeyState.waiting; private final Alarm myWaiterForSecondPress = new Alarm(); @@ -1982,6 +1983,16 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements myToolWindowsPane.stretchWidth(toolWindow, value); } + @Override + public boolean isMaximized(@NotNull ToolWindow wnd) { + return myToolWindowsPane.isMaximized(wnd); + } + + @Override + public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) { + myToolWindowsPane.setMaximized(wnd, maximized); + } + public void stretchHeight(ToolWindowImpl toolWindow, int value) { myToolWindowsPane.stretchHeight(toolWindow, value); } 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 d1232749d3c8..3606939167a8 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 @@ -22,20 +22,27 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.ThreeComponentsSplitter; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.wm.IdeGlassPaneUtil; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowAnchor; import com.intellij.openapi.wm.ToolWindowType; -import com.intellij.openapi.wm.ex.ToolWindowEx; import com.intellij.openapi.wm.impl.commands.FinalizableCommand; import com.intellij.ui.ScreenUtil; import com.intellij.ui.components.JBLayeredPane; import com.intellij.util.containers.HashMap; import com.intellij.util.ui.FadeInFadeOut; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; @@ -58,6 +65,7 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable { private final HashMap myButton2Info; private final HashMap myDecorator2Info; private final HashMap myId2SplitProportion; + private Pair myMaximizedProportion = null; /** * This panel is the layered pane where all sliding tool windows are located. The DEFAULT * layer contains splitters. The PALETTE layer contains all sliding tool windows. @@ -192,6 +200,24 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable { super.addNotify(); if (ScreenUtil.isStandardAddRemoveNotify(this)) { UISettings.getInstance().addUISettingsListener(myUISettingsListener, myDisposable); + IdeGlassPaneUtil.find(this).addMousePreprocessor(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2 && e.getModifiersEx() == 0) { + e = SwingUtilities.convertMouseEvent(e.getComponent(), e, ToolWindowsPane.this); + Component component = SwingUtilities.getDeepestComponentAt(ToolWindowsPane.this, e.getX(), e.getY()); + Component header = component == null ? null : UIUtil.findParentByCondition((JComponent)component, new Condition() { + @Override + public boolean value(Component component) { + return component instanceof ToolWindowHeader; + } + }); + if (header instanceof ToolWindowHeader) { + ((ToolWindowHeader)header).switchMaximizedState(myFrame.getProject()); + } + } + } + }, this); } } @@ -490,7 +516,21 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable { } private void stretch(ToolWindow wnd, int value) { - if (!wnd.isVisible()) return; + Pair pair = findResizerAndComponent(wnd); + if (pair == null) return; + + boolean vertical = wnd.getAnchor() == ToolWindowAnchor.TOP || wnd.getAnchor() == ToolWindowAnchor.BOTTOM; + int actualSize = (vertical ? pair.second.getHeight() : pair.second.getWidth()) + value; + boolean first = wnd.getAnchor() == ToolWindowAnchor.LEFT || wnd.getAnchor() == ToolWindowAnchor.TOP; + int maxValue = vertical ? myVerticalSplitter.getMaxSize(first) : myHorizontalSplitter.getMaxSize(first); + int minValue = vertical ? myVerticalSplitter.getMinSize(first) : myHorizontalSplitter.getMinSize(first);; + + pair.first.setSize(Math.max(minValue, Math.min(maxValue, actualSize))); + } + + @Nullable + private Pair findResizerAndComponent(ToolWindow wnd) { + if (!wnd.isVisible()) return null; Resizer resizer = null; Component cmp = null; @@ -534,26 +574,7 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable { } } - if (resizer == null) return; - - int currentValue = wnd.getAnchor().isHorizontal() ? cmp.getHeight() : cmp.getWidth(); - - int actualSize = currentValue + value; - - int minValue = - wnd.getAnchor().isHorizontal() ? ((ToolWindowEx)wnd).getDecorator().getHeaderHeight() : 16 + myHorizontalSplitter.getDividerWidth(); - int maxValue = wnd.getAnchor().isHorizontal() ? myLayeredPane.getHeight() : myLayeredPane.getWidth(); - - - if (actualSize < minValue) { - actualSize = minValue; - } - - if (actualSize > maxValue) { - actualSize = maxValue; - } - - resizer.setSize(actualSize); + return resizer != null ? Pair.create(resizer, cmp) : null; } private void updateLayout() { @@ -598,6 +619,27 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable { } } + public boolean isMaximized(@NotNull ToolWindow wnd) { + return myMaximizedProportion != null && myMaximizedProportion.first == wnd; + } + + public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) { + Pair resizerAndComponent = findResizerAndComponent(wnd); + if (resizerAndComponent == null) return; + + if (!maximized) { + ToolWindow maximizedWindow = myMaximizedProportion.first; + assert maximizedWindow == wnd; + resizerAndComponent.first.setSize(myMaximizedProportion.second); + myMaximizedProportion = null; + } else { + int size = wnd.getAnchor().isHorizontal() ? resizerAndComponent.second.getHeight() : resizerAndComponent.second.getWidth(); + stretch(wnd, Short.MAX_VALUE); + myMaximizedProportion = Pair.create(wnd, size); + } + doLayout(); + } + interface Resizer { void setSize(int size); diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 58adbf219537..138a5e3b60b5 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -842,6 +842,9 @@ action.ResizeToolWindowUp.text=Stretch to Top action.ResizeToolWindowUp.description=Resize active tool window to the top action.ResizeToolWindowDown.text=Stretch to Bottom action.ResizeToolWindowDown.description=Resize active tool window to the bottom +action.ResizeToolWindowMaximize.text=Maximize tool window +action.ResizeToolWindowMaximize.text.alternative=Restore tool window size +action.ResizeToolWindowMaximize.description=Maximize tool widow action.IncrementWindowWidth.text=Increment Width action.DecrementWindowWidth.text=Decrement Width action.IncrementWindowHeight.text=Increment Height diff --git a/platform/platform-resources/src/idea/Keymap_Default.xml b/platform/platform-resources/src/idea/Keymap_Default.xml index 405316ff8468..8907cc61f633 100644 --- a/platform/platform-resources/src/idea/Keymap_Default.xml +++ b/platform/platform-resources/src/idea/Keymap_Default.xml @@ -774,6 +774,9 @@ + + + diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index fb3b31747ae9..4e2657b427ee 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -355,6 +355,7 @@ + diff --git a/platform/testFramework/src/com/intellij/mock/Mock.java b/platform/testFramework/src/com/intellij/mock/Mock.java index a2768f2e1851..4ee338aba63c 100644 --- a/platform/testFramework/src/com/intellij/mock/Mock.java +++ b/platform/testFramework/src/com/intellij/mock/Mock.java @@ -654,6 +654,15 @@ public class Mock { return null; } + @Override + public boolean isMaximized(@NotNull ToolWindow wnd) { + return false; + } + + @Override + public void setMaximized(@NotNull ToolWindow wnd, boolean maximized) { + } + @Override public void notifyByBalloon(@NotNull final String toolWindowId, @NotNull final MessageType type, @NotNull final String htmlBody) { }