IDEA-112485 Shortcut to maximize toolwindow appcode/140.597 clion/140.603 dbe/140.602 idea/140.599 phpstorm/140.604 pycharm/140.598 rubymine/140.601 webstorm/140.600
authorVassiliy.Kudryashov <Vassiliy.Kudryashov@jetbrains.com>
Sun, 16 Nov 2014 22:20:47 +0000 (01:20 +0300)
committerVassiliy.Kudryashov <Vassiliy.Kudryashov@jetbrains.com>
Sun, 16 Nov 2014 22:22:07 +0000 (01:22 +0300)
12 files changed:
platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java
platform/platform-api/src/com/intellij/openapi/wm/ToolWindowManager.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/MaximizeToolWindowAction.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeadlessManagerImpl.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowManagerImpl.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java
platform/platform-resources-en/src/messages/ActionsBundle.properties
platform/platform-resources/src/idea/Keymap_Default.xml
platform/platform-resources/src/idea/PlatformActions.xml
platform/testFramework/src/com/intellij/mock/Mock.java

index 62864d01b91ac00ebbe8d3ea221908ce6ba7d689..e6311fbae473421fdfe04e7faef3ccd29cd1f923 100644 (file)
@@ -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()) {
index b616429a11c8fb89dc2e4271a18fbad8c583b98b..654f43e0c2ecdf979561f30696a5ab453cc147b2 100644 (file)
@@ -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);
 }
index bf9a015bdfeb3305826b2e3b527d5332093c515b..371d847f169907a83afaeee143a078f299761fa6 100644 (file)
@@ -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 (file)
index 0000000..29d68ad
--- /dev/null
@@ -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"));
+  }
+}
index 101d3ab2762be2d1bd5e258c73f46231b162a237..7d64b1180e15a474cd52361a2c86d0dcb954a855 100644 (file)
@@ -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();
index 7b9a7494c6405409959f353f8e8814cdb8ecf7cc..5fa360235cb22222500d0b5f183c3922daab5feb 100644 (file)
@@ -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) {
 
index c0c27ba8c128b4379084dc43f76f4a9f0b998e0e..8acebb5b5bfde7a40785abc161198052e39bfd06 100644 (file)
@@ -120,6 +120,7 @@ public final class ToolWindowManagerImpl extends ToolWindowManagerEx implements
   private final FileEditorManager myFileEditorManager;
   private final LafManager myLafManager;
   private final Map<String, Balloon> myWindow2Balloon = new HashMap<String, Balloon>();
+  private Pair<String, Integer> 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);
   }
index d1232749d3c81bb9bb86e0a45457a339bc6d01ca..3606939167a835d758ec71295b39bada6b4a5143 100644 (file)
@@ -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<StripeButton, WindowInfoImpl> myButton2Info;
   private final HashMap<InternalDecorator, WindowInfoImpl> myDecorator2Info;
   private final HashMap<String, Float> myId2SplitProportion;
+  private Pair<ToolWindow, Integer> 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<Component>() {
+              @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<Resizer, Component> 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<Resizer, Component> 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<Resizer, Component> 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);
index 58adbf2195373fdabf95f8a61af3442139c27811..138a5e3b60b527b7caadb61ca4cf81c674de7ffa 100644 (file)
@@ -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
index 405316ff8468802524ae75850ba95c0611666c57..8907cc61f6339845bd4c19b33bdea8f977de3ad2 100644 (file)
   <action id="ResizeToolWindowDown">
     <keyboard-shortcut first-keystroke="control shift DOWN"/>
   </action>
+  <action id="MaximizeToolWindow">
+    <keyboard-shortcut first-keystroke="control shift QUOTE"/>
+  </action>
   <action id="HideSideWindows"/>
   <action id="ShowPopupMenu">
     <keyboard-shortcut first-keystroke="CONTEXT_MENU"/>
index fb3b31747ae97d5f022d80a039ddb003733bc5f3..4e2657b427ee9a5def5cf9b73f02fa6030b7d421 100644 (file)
           <action id="HideAllWindows" class="com.intellij.ide.actions.HideAllToolWindowsAction"/>
           <action id="CloseActiveTab" class="com.intellij.ide.actions.CloseActiveTabAction"/>
           <action id="JumpToLastWindow" class="com.intellij.ide.actions.JumpToLastWindowAction"/>
+          <action id="MaximizeToolWindow" class="com.intellij.openapi.wm.impl.MaximizeToolWindowAction"/>
           <separator/>
           <action id="TogglePinnedMode" class="com.intellij.ide.actions.TogglePinnedModeAction"/>
           <action id="ToggleDockMode" class="com.intellij.ide.actions.ToggleDockModeAction"/>
index a2768f2e1851628d62f729941f0bd1f2b32bebb4..4ee338aba63c2b75944049bdc3327a8ae9899668 100644 (file)
@@ -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) {
     }