IDEA-149210 Rework IDE notifications
authorAlexander Lobas <Alexander.Lobas@jetbrains.com>
Fri, 11 Mar 2016 08:07:52 +0000 (11:07 +0300)
committerAlexander Lobas <Alexander.Lobas@jetbrains.com>
Fri, 11 Mar 2016 08:08:53 +0000 (11:08 +0300)
platform/platform-impl/src/com/intellij/notification/EventLog.java
platform/platform-impl/src/com/intellij/notification/impl/NotificationsManagerImpl.java
platform/platform-impl/src/com/intellij/ui/BalloonImpl.java
platform/platform-impl/src/com/intellij/ui/BalloonLayoutConfiguration.java
platform/platform-impl/src/com/intellij/ui/BalloonLayoutData.java
platform/platform-impl/src/com/intellij/ui/NotificationBalloonActionProvider.java
platform/platform-impl/src/com/intellij/ui/NotificationBalloonShadowBorderProvider.java

index f239624650c3d8afdeeb4cc0806a6006370bec90..f816edb4a67b3d0cae26b70c81bbdefc34e724ce 100644 (file)
@@ -31,12 +31,10 @@ import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectManager;
 import com.intellij.openapi.startup.StartupManager;
 import com.intellij.openapi.ui.popup.Balloon;
-import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.ShutDownTracker;
-import com.intellij.openapi.util.TextRange;
-import com.intellij.openapi.util.Trinity;
+import com.intellij.openapi.util.*;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.wm.*;
+import com.intellij.ui.BalloonLayoutData;
 import com.intellij.ui.awt.RelativePoint;
 import com.intellij.ui.content.Content;
 import com.intellij.util.Function;
@@ -538,13 +536,20 @@ public class EventLog {
       if (target != null) {
         IdeFrame frame = WindowManager.getInstance().getIdeFrame(project);
         assert frame != null;
-        Balloon balloon = NotificationsManagerImpl.createBalloon(frame, myNotification, true, true, null, project);
+        Ref<Object> layoutDataRef = null;
+        if (NotificationsManagerImpl.newEnabled()) {
+          BalloonLayoutData layoutData = new BalloonLayoutData();
+          layoutData.showFullContent = true;
+          layoutData.showSettingButton = false;
+          layoutDataRef = new Ref<>(layoutData);
+        }
+        Balloon balloon = NotificationsManagerImpl.createBalloon(frame, myNotification, true, true, layoutDataRef, project);
         balloon.show(target, Balloon.Position.above);
       }
     }
 
-    private static void hideBalloon(Notification notification1) {
-      Balloon balloon = notification1.getBalloon();
+    private static void hideBalloon(Notification notification) {
+      Balloon balloon = notification.getBalloon();
       if (balloon != null) {
         balloon.hide(true);
       }
index e56eca06b4027da70634fca3f33682df4cbebaff..4fef2b936ee86bb5900ebf7d6871f28a5a82407f 100644 (file)
@@ -308,9 +308,7 @@ public class NotificationsManagerImpl extends NotificationsManager {
                                       @Nullable Ref<Object> layoutDataRef,
                                       @NotNull Disposable parentDisposable) {
     if (layoutDataRef != null) {
-      Balloon balloon = createNewBalloon(windowComponent, notification, showCallout, hideOnClickOutside, layoutDataRef);
-      Disposer.register(parentDisposable, balloon);
-      return balloon;
+      return createNewBalloon(windowComponent, notification, showCallout, hideOnClickOutside, layoutDataRef, parentDisposable);
     }
 
     final JEditorPane text = new JEditorPane();
@@ -463,10 +461,13 @@ public class NotificationsManagerImpl extends NotificationsManager {
                                           @NotNull Notification notification,
                                           boolean showCallout,
                                           boolean hideOnClickOutside,
-                                          @NotNull Ref<Object> layoutDataRef) {
-    final BalloonLayoutData layoutData = new BalloonLayoutData();
+                                          @NotNull Ref<Object> layoutDataRef,
+                                          @NotNull Disposable parentDisposable) {
+    final BalloonLayoutData layoutData = layoutDataRef.isNull() ? new BalloonLayoutData() : (BalloonLayoutData)layoutDataRef.get();
     layoutDataRef.set(layoutData);
 
+    boolean showFullContent = layoutData.showFullContent || notification instanceof NotificationActionProvider;
+
     Color foregroundR = Gray._0;
     Color foregroundD = Gray._191;
     final Color foreground = new JBColor(foregroundR, foregroundD);
@@ -498,6 +499,10 @@ public class NotificationsManagerImpl extends NotificationsManager {
     int prefSize = new JLabel(NotificationsUtil.buildHtml(notification, null, true, null, fontStyle)).getPreferredSize().width;
     String style = prefSize > BalloonLayoutConfiguration.MaxWidth ? BalloonLayoutConfiguration.MaxWidthStyle : null;
 
+    if (layoutData.showFullContent) {
+      style = prefSize > BalloonLayoutConfiguration.MaxFullContentWidth ? BalloonLayoutConfiguration.MaxFullContentWidthStyle : null;
+    }
+
     String textR = NotificationsUtil.buildHtml(notification, style, true, foregroundR, fontStyle);
     String textD = NotificationsUtil.buildHtml(notification, style, true, foregroundD, fontStyle);
     LafHandler lafHandler = new LafHandler(text, textR, textD);
@@ -549,8 +554,6 @@ public class NotificationsManagerImpl extends NotificationsManager {
     layoutData.maxScrollHeight = Math.min(layoutData.fullHeight, calculateContentHeight(10));
     layoutData.configuration = BalloonLayoutConfiguration.create(notification, layoutData);
 
-    boolean showFullContent = notification instanceof NotificationActionProvider;
-
     if (!showFullContent && layoutData.maxScrollHeight != layoutData.fullHeight) {
       pane.setViewport(new GradientViewport(text, JBUI.insets(10, 0), true) {
         @Nullable
@@ -731,6 +734,7 @@ public class NotificationsManagerImpl extends NotificationsManager {
         new NotificationBalloonActionProvider(balloon, layout.getTitle(), layoutData, notification.getGroupId()));
     }
 
+    Disposer.register(parentDisposable, balloon);
     return balloon;
   }
 
@@ -977,7 +981,9 @@ public class NotificationsManagerImpl extends NotificationsManager {
       int actionWidth = actionSize.width + expandSize.width;
 
       int width = Math.max(centerWidth, Math.max(titleWidth, actionWidth));
-      width = Math.min(width, BalloonLayoutConfiguration.MaxWidth);
+      if (!myLayoutData.showFullContent) {
+        width = Math.min(width, BalloonLayoutConfiguration.MaxWidth);
+      }
       width = Math.max(width, BalloonLayoutConfiguration.MinWidth);
 
       return new Dimension(width, height);
index cf0d89d8a3ee7b19914a1f386a173bf9c2a8102e..0c362a72a8c15d512b4c452f6069ac0ee64e2be5 100644 (file)
@@ -101,8 +101,8 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
 
   private Rectangle myForcedBounds;
 
-  //private CloseButton myCloseRec;
   private ActionProvider myActionProvider;
+  private List<ActionButton> myActionButtons;
 
   private final AWTEventListener myAwtActivityListener = new AWTEventListener() {
     @Override
@@ -227,8 +227,10 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
 
     if (!cmp.isShowing()) return true;
     if (cmp instanceof MenuElement) return false;
-    for (ActionButton button : myActionProvider.getActions()) {
-      if (cmp == button) return true;
+    if (myActionButtons != null) {
+      for (ActionButton button : myActionButtons) {
+        if (cmp == button) return true;
+      }
     }
     if (UIUtil.isDescendingFrom(cmp, myComp)) return true;
     if (myComp == null || !myComp.isShowing()) return false;
@@ -358,9 +360,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
 
   @Override
   public void show(final RelativePoint target, final Balloon.Position position) {
-    AbstractPosition pos = getAbstractPositionFor(position);
-
-    show(target, pos);
+    show(target, getAbstractPositionFor(position));
   }
 
   public int getLayer() {
@@ -378,43 +378,23 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
   }
 
   private static AbstractPosition getAbstractPositionFor(Position position) {
-    AbstractPosition pos = BELOW;
     switch (position) {
       case atLeft:
-        pos = AT_LEFT;
-        break;
+        return AT_LEFT;
       case atRight:
-        pos = AT_RIGHT;
-        break;
+        return AT_RIGHT;
       case below:
-        pos = BELOW;
-        break;
+        return BELOW;
       case above:
-        pos = ABOVE;
-        break;
+        return ABOVE;
+      default:
+        return BELOW;
     }
-    return pos;
   }
 
   @Override
   public void show(PositionTracker<Balloon> tracker, Balloon.Position position) {
-    AbstractPosition pos = BELOW;
-    switch (position) {
-      case atLeft:
-        pos = AT_LEFT;
-        break;
-      case atRight:
-        pos = AT_RIGHT;
-        break;
-      case below:
-        pos = BELOW;
-        break;
-      case above:
-        pos = ABOVE;
-        break;
-    }
-
-    show(tracker, pos);
+    show(tracker, getAbstractPositionFor(position));
   }
 
   private Insets getInsetsCopy() {
@@ -625,7 +605,9 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
 
   private Dimension getContentSizeFor(AbstractPosition position) {
     Dimension size = myContent.getPreferredSize();
-    JBInsets.addTo(size, position.createBorder(this).getBorderInsets());
+    if (myShadowBorderProvider == null) {
+      JBInsets.addTo(size, position.createBorder(this).getBorderInsets());
+    }
     return size;
   }
 
@@ -658,11 +640,12 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
       };
 
       myActionProvider = new ActionProvider() {
-        private ActionButton myCloseButton = new CloseButton(listener);
+        private ActionButton myCloseButton;
 
         @NotNull
         @Override
-        public List<ActionButton> getActions() {
+        public List<ActionButton> createActions() {
+          myCloseButton = new CloseButton(listener);
           return Collections.singletonList(myCloseButton);
         }
 
@@ -765,6 +748,8 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
     void paintShadow(@NotNull JComponent component, @NotNull Graphics g);
 
     void paintBorder(@NotNull Rectangle bounds, @NotNull Graphics2D g);
+
+    void paintPointingShape(@NotNull Rectangle bounds, @NotNull Point pointTarget, @NotNull Position position, @NotNull Graphics2D g);
   }
 
   @Override
@@ -1013,7 +998,8 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
                                       Rectangle forcedBounds,
                                       Dimension preferredSize,
                                       boolean showPointer,
-                                      Point point, Insets containerInsets) {
+                                      Point point,
+                                      Insets containerInsets) {
 
       Rectangle bounds = forcedBounds;
 
@@ -1037,6 +1023,22 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
 
       if (balloon.myShadowBorderProvider != null) {
         balloon.myShadowBorderProvider.paintBorder(bounds, g);
+        if (balloon.myShowPointer) {
+          Position position;
+          if (this == ABOVE) {
+            position = Position.above;
+          }
+          else if (this == BELOW) {
+            position = Position.below;
+          }
+          else if (this == AT_LEFT) {
+            position = Position.atLeft;
+          }
+          else {
+            position = Position.atRight;
+          }
+          balloon.myShadowBorderProvider.paintPointingShape(bounds, pointTarget, position, g);
+        }
         cfg.restore();
         return;
       }
@@ -1095,7 +1097,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
       if (bounds.x < targetPoint.x &&
           bounds.x + bounds.width > targetPoint.x &&
           bounds.y < targetPoint.y &&
-          bounds.y + bounds.height < targetPoint.y) {
+          bounds.y + bounds.height > targetPoint.y) {
         return false;
       }
 
@@ -1398,7 +1400,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
 
   public interface ActionProvider {
     @NotNull
-    List<ActionButton> getActions();
+    List<ActionButton> createActions();
 
     void layout(@NotNull Rectangle bounds);
   }
@@ -1666,15 +1668,19 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
         return;
       }
 
-      //noinspection SSBasedInspection
-      SwingUtilities.invokeLater(new Runnable() {
-        @Override
-        public void run() {
-          for (ActionButton button : myActionProvider.getActions()) {
-            disposeButton(button);
+      final List<ActionButton> buttons = myActionButtons;
+      myActionButtons = null;
+      if (buttons != null) {
+        //noinspection SSBasedInspection
+        SwingUtilities.invokeLater(new Runnable() {
+          @Override
+          public void run() {
+            for (ActionButton button : buttons) {
+              disposeButton(button);
+            }
           }
-        }
-      });
+        });
+      }
     }
 
     public void setAlpha(float alpha) {
@@ -1692,7 +1698,11 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
 
 
       if (getParent() != null) {
-        for (ActionButton button : myActionProvider.getActions()) {
+        if (myActionButtons == null) {
+          myActionButtons = myActionProvider.createActions();
+        }
+
+        for (ActionButton button : myActionButtons) {
           if (button.getParent() == null) {
             myLayeredPane.add(button);
             myLayeredPane.setLayer(button, JLayeredPane.DRAG_LAYER);
@@ -1702,7 +1712,8 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
 
       if (isVisible()) {
         Rectangle lpBounds = SwingUtilities.convertRectangle(getParent(), bounds, myLayeredPane);
-        lpBounds = myPosition.getPointlessContentRec(lpBounds, myBalloon.getPointerLength(myPosition));
+        lpBounds = myPosition
+          .getPointlessContentRec(lpBounds, myBalloon.myShadowBorderProvider == null ? myBalloon.getPointerLength(myPosition) : 0);
         myActionProvider.layout(lpBounds);
       }
 
@@ -1718,8 +1729,10 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui {
     }
 
     public void repaintButton() {
-      for (ActionButton button : myActionProvider.getActions()) {
-        button.repaint();
+      if (myActionButtons != null) {
+        for (ActionButton button : myActionButtons) {
+          button.repaint();
+        }
       }
     }
   }
index a719e41ed29d70e8722692a4dd2b52f45bf86dd0..55a6ae998b06bf64c98c8d1eeabc08632a21fac5 100644 (file)
@@ -49,6 +49,9 @@ public class BalloonLayoutConfiguration {
   public static final int FixedWidth;
   public static final int MaxWidth;
 
+  public static final int MaxFullContentWidth = JBUI.scale(350);
+  public static final String MaxFullContentWidthStyle = "width:" + MaxFullContentWidth + "px;";
+
   public static final int MinWidth = JBUI.scale(100);
 
   public static final String MaxWidthStyle;
index cb60598c039868c9ea754dfb3e1812ca45170870..e1e7e74c14a878e22c1310c9f7148bf01e22d5ce 100644 (file)
@@ -22,6 +22,8 @@ import com.intellij.openapi.util.Computable;
  * @author Alexander Lobas
  */
 public class BalloonLayoutData {
+  public boolean showFullContent;
+
   public int height;
   public int twoLineHeight;
   public int fullHeight;
@@ -31,6 +33,8 @@ public class BalloonLayoutData {
 
   public Runnable closeAll;
   public Runnable doLayout;
+
+  public boolean showSettingButton = true;
   public Computable<Boolean> showActions;
 
   public Project project;
index 736fc07420b724b204d3a36dd568c47bc3565e69..af1f2f910e9f44bb153333305be01f981a9c0b36 100644 (file)
@@ -40,9 +40,9 @@ public class NotificationBalloonActionProvider implements BalloonImpl.ActionProv
   private final BalloonLayoutData myLayoutData;
   private final String myDisplayGroupId;
   private final Component myRepaintPanel;
-  private final BalloonImpl.ActionButton mySettingButton;
-  private final BalloonImpl.ActionButton myCloseButton;
-  private final List<BalloonImpl.ActionButton> myActions = new ArrayList<BalloonImpl.ActionButton>();
+  private BalloonImpl.ActionButton mySettingButton;
+  private BalloonImpl.ActionButton myCloseButton;
+  private List<BalloonImpl.ActionButton> myActions;
 
   private static final Rectangle CloseHoverBounds = new JBRectangle(5, 5, 12, 10);
 
@@ -54,8 +54,15 @@ public class NotificationBalloonActionProvider implements BalloonImpl.ActionProv
     myDisplayGroupId = displayGroupId;
     myBalloon = balloon;
     myRepaintPanel = repaintPanel;
+  }
+
+  @NotNull
+  @Override
+  public List<BalloonImpl.ActionButton> createActions() {
+    myActions = new ArrayList<BalloonImpl.ActionButton>();
 
-    if (myDisplayGroupId == null || !NotificationsConfigurationImpl.getInstanceImpl().isRegistered(myDisplayGroupId)) {
+    if (!myLayoutData.showSettingButton || myDisplayGroupId == null ||
+        !NotificationsConfigurationImpl.getInstanceImpl().isRegistered(myDisplayGroupId)) {
       mySettingButton = null;
     }
     else {
@@ -85,8 +92,8 @@ public class NotificationBalloonActionProvider implements BalloonImpl.ActionProv
       };
       myActions.add(mySettingButton);
 
-      if (repaintPanel != null) {
-        layoutData.showActions = new Computable<Boolean>() {
+      if (myRepaintPanel != null) {
+        myLayoutData.showActions = new Computable<Boolean>() {
           @Override
           public Boolean compute() {
             for (BalloonImpl.ActionButton action : myActions) {
@@ -127,11 +134,7 @@ public class NotificationBalloonActionProvider implements BalloonImpl.ActionProv
       }
     };
     myActions.add(myCloseButton);
-  }
 
-  @NotNull
-  @Override
-  public List<BalloonImpl.ActionButton> getActions() {
     return myActions;
   }
 
index a21534e6f81d5555ff8edad817932848383127e9..9d465d2ced198413a256b6f92f154e1943591d5e 100644 (file)
@@ -16,6 +16,7 @@
 package com.intellij.ui;
 
 import com.intellij.icons.AllIcons.Ide.Notification.Shadow;
+import com.intellij.openapi.ui.popup.Balloon;
 import com.intellij.util.ui.JBInsets;
 import org.jetbrains.annotations.NotNull;
 
@@ -110,4 +111,77 @@ public class NotificationBalloonShadowBorderProvider implements BalloonImpl.Shad
     g.setColor(myBorderColor);
     g.draw(new RoundRectangle2D.Double(bounds.x + 0.5, bounds.y + 0.5, bounds.width - 1, bounds.height - 1, 3, 3));
   }
+
+  @Override
+  public void paintPointingShape(@NotNull Rectangle bounds,
+                                 @NotNull Point pointTarget,
+                                 @NotNull Balloon.Position position,
+                                 @NotNull Graphics2D g) {
+    int x, y, length;
+
+    if (position == Balloon.Position.above) {
+      length = INSETS.bottom;
+      x = pointTarget.x;
+      y = bounds.y + bounds.height + length;
+    }
+    else if (position == Balloon.Position.below) {
+      length = INSETS.top;
+      x = pointTarget.x;
+      y = bounds.y - length;
+    }
+    else if (position == Balloon.Position.atRight) {
+      length = INSETS.left;
+      x = bounds.x - length;
+      y = pointTarget.y;
+    }
+    else {
+      length = INSETS.right;
+      x = bounds.x + bounds.width + length;
+      y = pointTarget.y;
+    }
+
+    Polygon p = new Polygon();
+    p.addPoint(x, y);
+
+    length += 2;
+    if (position == Balloon.Position.above) {
+      p.addPoint(x - length, y - length);
+      p.addPoint(x + length, y - length);
+    }
+    else if (position == Balloon.Position.below) {
+      p.addPoint(x - length, y + length);
+      p.addPoint(x + length, y + length);
+    }
+    else if (position == Balloon.Position.atRight) {
+      p.addPoint(x + length, y - length);
+      p.addPoint(x + length, y + length);
+    }
+    else {
+      p.addPoint(x - length, y - length);
+      p.addPoint(x - length, y + length);
+    }
+
+    g.setColor(myFillColor);
+    g.fillPolygon(p);
+
+    g.setColor(myBorderColor);
+
+    length -= 2;
+    if (position == Balloon.Position.above) {
+      g.drawLine(x, y, x - length, y - length);
+      g.drawLine(x, y, x + length, y - length);
+    }
+    else if (position == Balloon.Position.below) {
+      g.drawLine(x, y, x - length, y + length);
+      g.drawLine(x, y, x + length, y + length);
+    }
+    else if (position == Balloon.Position.atRight) {
+      g.drawLine(x, y, x + length, y - length);
+      g.drawLine(x, y, x + length, y + length);
+    }
+    else {
+      g.drawLine(x, y, x - length, y - length);
+      g.drawLine(x, y, x - length, y + length);
+    }
+  }
 }
\ No newline at end of file