IDEA-112485 Shortcut to maximize toolwindow : improvements
authorVassiliy.Kudryashov <Vassiliy.Kudryashov@jetbrains.com>
Wed, 26 Nov 2014 13:18:40 +0000 (16:18 +0300)
committerVassiliy.Kudryashov <Vassiliy.Kudryashov@jetbrains.com>
Wed, 26 Nov 2014 13:18:40 +0000 (16:18 +0300)
Add "resize" and "move to" actions to "Gear" tool window button
Rollback to old tool window animation

platform/platform-api/src/com/intellij/openapi/ui/ThreeComponentsSplitter.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowHeader.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/ToolWindowsPane.java

index e6311fbae473421fdfe04e7faef3ccd29cd1f923..5e92dc130e607b629ffe60a21ac538410d33b8e3 100644 (file)
@@ -37,7 +37,7 @@ import java.awt.event.MouseEvent;
  * @author Vladimir Kondratyev
  */
 public class ThreeComponentsSplitter extends JPanel implements Disposable {
-  public static final int MIN_SIZE = 48;
+  public static final int MIN_SIZE = 0;
 
   private int myDividerWidth;
   /**
@@ -63,8 +63,8 @@ public class ThreeComponentsSplitter extends JPanel implements Disposable {
   @Nullable private JComponent myInnerComponent;
   @Nullable private JComponent myLastComponent;
 
-  private int myFirstSize = 10;
-  private int myLastSize = 10;
+  private int myFirstSize = 0;
+  private int myLastSize = 0;
 
   private boolean myShowDividerControls;
   private int myDividerZone;
index 371d847f169907a83afaeee143a078f299761fa6..876d4e8c5886c70fab2582a106e437b9e8bc4147 100644 (file)
@@ -99,7 +99,7 @@ public final class InternalDecorator extends JPanel implements Queryable, DataPr
     myHeader = new ToolWindowHeader(toolWindow, info, new Producer<ActionGroup>() {
       @Override
       public ActionGroup produce() {
-        return createGearPopupGroup();
+        return /*createGearPopupGroup()*/createPopupGroup(true);
       }
     }) {
       @Override
@@ -370,6 +370,10 @@ public final class InternalDecorator extends JPanel implements Queryable, DataPr
 
 
   public final ActionGroup createPopupGroup() {
+    return createPopupGroup(false);
+  }
+
+  public final ActionGroup createPopupGroup(boolean skipHideAction) {
     final DefaultActionGroup group = createGearPopupGroup();
     if (!ToolWindowId.PREVIEW.equals(myInfo.getId())) {
       group.add(myToggleContentUiTypeAction);
@@ -405,9 +409,10 @@ public final class InternalDecorator extends JPanel implements Queryable, DataPr
     resize.add(ActionManager.getInstance().getAction("MaximizeToolWindow"));
 
     group.add(resize);
-
-    group.addSeparator();
-    group.add(new HideAction());
+    if (!skipHideAction) {
+      group.addSeparator();
+      group.add(new HideAction());
+    }
     return group;
   }
 
index 7d64b1180e15a474cd52361a2c86d0dcb954a855..e3ebfe83a4b39614d5af24b9bb98d00a1f82b352 100644 (file)
@@ -25,13 +25,12 @@ 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.DoubleClickListener;
 import com.intellij.ui.InplaceButton;
 import com.intellij.ui.PopupHandler;
 import com.intellij.ui.UIBundle;
@@ -91,7 +90,7 @@ public abstract class ToolWindowHeader extends JPanel implements Disposable, UIS
           Component c = getComponent(0);
           Dimension size = c.getPreferredSize();
           if (size.width < (r.width - insets.left - insets.right)) {
-            c.setBounds(insets.left, insets.top, r.width, r.height - insets.top - insets.bottom);
+            c.setBounds(insets.left, insets.top, size.width, r.height - insets.top - insets.bottom);
           } else {
             c.setBounds(insets.left, insets.top, r.width - insets.left - insets.right, r.height - insets.top - insets.bottom);
           }
@@ -169,7 +168,7 @@ public abstract class ToolWindowHeader extends JPanel implements Disposable, UIS
     addDefaultActions(eastPanel);
     myButtonPanel = eastPanel;
 
-    addMouseListener(new PopupHandler() {
+    westPanel.addMouseListener(new PopupHandler() {
       public void invokePopup(final Component comp, final int x, final int y) {
         toolWindow.getContentUI().showContextMenu(comp, x, y, toolWindow.getPopupGroup(), toolWindow.getContentManager().getSelectedContent());
       }
@@ -210,12 +209,14 @@ 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));
+    new DoubleClickListener(){
+      @Override
+      protected boolean onDoubleClick(MouseEvent event) {
+        ToolWindowManagerImpl mgr = toolWindow.getToolWindowManager();
+        mgr.setMaximized(myToolWindow, !mgr.isMaximized(myToolWindow));
+        return true;
+      }
+    }.installOn(westPanel);
   }
 
   @Override
index ffab6a556285a96fde1271473840f5e64f599bcd..2df9549e8ceecdbacbaf18105a453f074e8370bb 100644 (file)
@@ -22,28 +22,25 @@ 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.SystemInfo;
 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.impl.commands.FinalizableCommand;
+import com.intellij.reference.SoftReference;
 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.ActionListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
+import java.awt.image.BufferedImage;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.util.ArrayList;
@@ -201,24 +198,6 @@ 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 || component instanceof ActionListener;
-              }
-            });
-            if (header instanceof ToolWindowHeader) {
-              ((ToolWindowHeader)header).switchMaximizedState(myFrame.getProject());
-            }
-          }
-        }
-      }, this);
     }
   }
 
@@ -891,36 +870,60 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
       myInfo = info;
       myDirtyMode = dirtyMode;
     }
-
     public final void run() {
-      // Show component.
-      final UISettings uiSettings = UISettings.getInstance();
-      if (!myDirtyMode && uiSettings.ANIMATE_WINDOWS && !UISettings.isRemoteDesktopConnected()) {
-        myLayeredPane.add(myComponent, JLayeredPane.PALETTE_LAYER);
-        myLayeredPane.moveToFront(myComponent);
-        myLayeredPane.setBoundsInPaletteLayer(myComponent, myInfo.getAnchor(), myInfo.getWeight());
-        final FadeInFadeOut fadeIn = new FadeInFadeOut(myComponent, 250, true, myId2Button.get(myInfo.getId()));
-        add(fadeIn, FadeInFadeOut.LAYER);
-        fadeIn.setBounds(0, 0, getWidth(), getHeight());
-        myLayeredPane.remove(myComponent);
-        fadeIn.doAnimation(new Runnable() {
-          @Override
-          public void run() {
-            remove(fadeIn);
+      try {
+        // Show component.
+        final UISettings uiSettings = UISettings.getInstance();
+        if (!myDirtyMode && uiSettings.ANIMATE_WINDOWS && !UISettings.isRemoteDesktopConnected()) {
+          // Prepare top image. This image is scrolling over bottom image.
+          final Image topImage = myLayeredPane.getTopImage();
+          final Graphics topGraphics = topImage.getGraphics();
+
+          Rectangle bounds;
+
+          try {
             myLayeredPane.add(myComponent, JLayeredPane.PALETTE_LAYER);
-            //myComponent.requestFocus();
-            repaint();
-            finish();
+            myLayeredPane.moveToFront(myComponent);
+            myLayeredPane.setBoundsInPaletteLayer(myComponent, myInfo.getAnchor(), myInfo.getWeight());
+            bounds = myComponent.getBounds();
+            myComponent.paint(topGraphics);
+            myLayeredPane.remove(myComponent);
           }
-        });
-      }
-      else { // not animated
-        myLayeredPane.add(myComponent, JLayeredPane.PALETTE_LAYER);
-        myLayeredPane.setBoundsInPaletteLayer(myComponent, myInfo.getAnchor(), myInfo.getWeight());
+          finally {
+            topGraphics.dispose();
+          }
+          // Prepare bottom image.
+          final Image bottomImage = myLayeredPane.getBottomImage();
+          final Graphics bottomGraphics = bottomImage.getGraphics();
+          try {
+            bottomGraphics.setClip(0, 0, bounds.width, bounds.height);
+            bottomGraphics.translate(-bounds.x, -bounds.y);
+            myLayeredPane.paint(bottomGraphics);
+          }
+          finally {
+            bottomGraphics.dispose();
+          }
+          // Start animation.
+          final Surface surface = new Surface(topImage, bottomImage, 1, myInfo.getAnchor(), uiSettings.ANIMATION_SPEED);
+          myLayeredPane.add(surface, JLayeredPane.PALETTE_LAYER);
+          surface.setBounds(bounds);
+          myLayeredPane.validate();
+          myLayeredPane.repaint();
+
+          surface.runMovement();
+          myLayeredPane.remove(surface);
+          myLayeredPane.add(myComponent, JLayeredPane.PALETTE_LAYER);
+        }
+        else { // not animated
+          myLayeredPane.add(myComponent, JLayeredPane.PALETTE_LAYER);
+          myLayeredPane.setBoundsInPaletteLayer(myComponent, myInfo.getAnchor(), myInfo.getWeight());
+        }
         if (!myDirtyMode) {
-          myLayeredPane.revalidate();
+          myLayeredPane.validate();
           myLayeredPane.repaint();
         }
+      }
+      finally {
         finish();
       }
     }
@@ -1076,30 +1079,53 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
       myInfo = info;
       myDirtyMode = dirtyMode;
     }
-
     public final void run() {
-      final UISettings uiSettings = UISettings.getInstance();
-      if (!myDirtyMode && uiSettings.ANIMATE_WINDOWS && !UISettings.isRemoteDesktopConnected()) {
-        // Remove component from the layered pane and start animation.
-        final FadeInFadeOut fadeOut = new FadeInFadeOut(myComponent, 450, false, getButtonById(myInfo.getId()));
-        add(fadeOut, FadeInFadeOut.LAYER);
-        fadeOut.setBounds(0, 0, getWidth(), getHeight());
-        myLayeredPane.remove(myComponent);
-        fadeOut.doAnimation(new Runnable() {
-          @Override
-          public void run() {
-            remove(fadeOut);
-            repaint();
-            finish();
+      try {
+        final UISettings uiSettings = UISettings.getInstance();
+        if (!myDirtyMode && uiSettings.ANIMATE_WINDOWS && !UISettings.isRemoteDesktopConnected()) {
+          final Rectangle bounds = myComponent.getBounds();
+          // Prepare top image. This image is scrolling over bottom image. It contains
+          // picture of component is being removed.
+          final Image topImage = myLayeredPane.getTopImage();
+          final Graphics topGraphics = topImage.getGraphics();
+          try {
+            myComponent.paint(topGraphics);
           }
-        });
-      }
-      else { // not animated
-        myLayeredPane.remove(myComponent);
+          finally {
+            topGraphics.dispose();
+          }
+          // Prepare bottom image. This image contains picture of component that is located
+          // under the component to is being removed.
+          final Image bottomImage = myLayeredPane.getBottomImage();
+          final Graphics bottomGraphics = bottomImage.getGraphics();
+          try {
+            myLayeredPane.remove(myComponent);
+            bottomGraphics.clipRect(0, 0, bounds.width, bounds.height);
+            bottomGraphics.translate(-bounds.x, -bounds.y);
+            myLayeredPane.paint(bottomGraphics);
+          }
+          finally {
+            bottomGraphics.dispose();
+          }
+          // Remove component from the layered pane and start animation.
+          final Surface surface = new Surface(topImage, bottomImage, -1, myInfo.getAnchor(), uiSettings.ANIMATION_SPEED * 2);
+          myLayeredPane.add(surface, JLayeredPane.PALETTE_LAYER);
+          surface.setBounds(bounds);
+          myLayeredPane.validate();
+          myLayeredPane.repaint();
+
+          surface.runMovement();
+          myLayeredPane.remove(surface);
+        }
+        else { // not animated
+          myLayeredPane.remove(myComponent);
+        }
         if (!myDirtyMode) {
-          myLayeredPane.revalidate();
+          myLayeredPane.validate();
           myLayeredPane.repaint();
         }
+      }
+      finally {
         finish();
       }
     }
@@ -1166,18 +1192,58 @@ public final class ToolWindowsPane extends JBLayeredPane implements Disposable {
       updateLayout();
     }
   }
-
   private final class MyLayeredPane extends JBLayeredPane {
     /*
      * These images are used to perform animated showing and hiding of components.
      * They are the member for performance reason.
      */
+    private SoftReference<BufferedImage> myBottomImageRef;
+    private SoftReference<BufferedImage> myTopImageRef;
 
     public MyLayeredPane(final JComponent splitter) {
+      myBottomImageRef = new SoftReference<BufferedImage>(null);
+      myTopImageRef = new SoftReference<BufferedImage>(null);
       setOpaque(false);
       add(splitter, JLayeredPane.DEFAULT_LAYER);
     }
 
+    public final Image getBottomImage() {
+      Pair<BufferedImage, SoftReference<BufferedImage>> result = getImage(myBottomImageRef);
+      myBottomImageRef = result.second;
+      return result.first;
+    }
+
+    public final Image getTopImage() {
+      Pair<BufferedImage, SoftReference<BufferedImage>> result = getImage(myTopImageRef);
+      myTopImageRef = result.second;
+      return result.first;
+    }
+
+    private Pair<BufferedImage, SoftReference<BufferedImage>> getImage(SoftReference<BufferedImage> imageRef) {
+      LOG.assertTrue(UISettings.getInstance().ANIMATE_WINDOWS);
+      BufferedImage image = imageRef.get();
+      if (
+        image == null ||
+        image.getWidth(null) < getWidth() || image.getHeight(null) < getHeight()
+        ) {
+        final int width = Math.max(Math.max(1, getWidth()), myFrame.getWidth());
+        final int height = Math.max(Math.max(1, getHeight()), myFrame.getHeight());
+        if (SystemInfo.isWindows) {
+          image = myFrame.getGraphicsConfiguration().createCompatibleImage(width, height);
+        }
+        else {
+          // Under Linux we have found that images created by createCompatibleImage(),
+          // createVolatileImage(), etc extremely slow for rendering. TrueColor buffered image
+          // is MUCH faster.
+          // On Mac we create a retina-compatible image
+
+          image = UIUtil.createImage(width, height, BufferedImage.TYPE_INT_RGB);
+        }
+        imageRef = new SoftReference<BufferedImage>(image);
+      }
+      return Pair.create(image, imageRef);
+    }
+
     /**
      * When component size becomes larger then bottom and top images should be enlarged.
      */