hint tooltips - graphite
authorKirill Kalishev <kirill.kalishev@jetbrains.com>
Fri, 10 Sep 2010 17:52:36 +0000 (21:52 +0400)
committerKirill Kalishev <kirill.kalishev@jetbrains.com>
Fri, 10 Sep 2010 17:52:36 +0000 (21:52 +0400)
platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonTooltipRendererProvider.java
platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/TrafficLightRenderer.java
platform/lang-impl/src/com/intellij/codeInsight/hint/InspectionDescriptionLinkHandler.java
platform/platform-impl/src/com/intellij/codeInsight/hint/LineTooltipRenderer.java
platform/platform-impl/src/com/intellij/codeInsight/hint/TooltipController.java
platform/platform-impl/src/com/intellij/ide/IdeTooltip.java
platform/platform-impl/src/com/intellij/ide/IdeTooltipManager.java
platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java
platform/platform-impl/src/com/intellij/ui/BalloonImpl.java
platform/platform-impl/src/com/intellij/ui/LightweightHint.java
platform/util/src/com/intellij/util/ui/UIUtil.java

index 34c25858f2abf430cbe83d0bcef022f95f13324c..64057b9214c64b5a90e9485308976964aaae098a 100644 (file)
@@ -34,6 +34,7 @@ import com.intellij.openapi.keymap.KeymapUtil;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.util.SmartList;
+import com.intellij.util.ui.UIUtil;
 import gnu.trove.THashSet;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
@@ -88,10 +89,10 @@ public class DaemonTooltipRendererProvider implements ErrorStripTooltipRendererP
       });
       final HighlightInfoComposite composite = new HighlightInfoComposite(infos);
       if (bigRenderer == null) {
-        bigRenderer = new MyRenderer(composite.toolTip);
+        bigRenderer = new MyRenderer(UIUtil.convertSpace2Nbsp(composite.toolTip));
       }
       else {
-        final LineTooltipRenderer renderer = new MyRenderer(composite.toolTip);
+        final LineTooltipRenderer renderer = new MyRenderer(UIUtil.convertSpace2Nbsp(composite.toolTip));
         renderer.addBelow(bigRenderer.getText());
         bigRenderer = renderer;
       }
index f9e9a8f08de7639101094dfd6b32dc303294767f..eb064fc2e1cc0f3e414248909a799fe13ecde8ec 100644 (file)
@@ -32,6 +32,7 @@ import com.intellij.ui.LayeredIcon;
 import com.intellij.util.ArrayUtil;
 import com.intellij.util.Processor;
 import com.intellij.util.ui.EmptyIcon;
+import com.intellij.util.ui.UIUtil;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.Nullable;
 
@@ -267,7 +268,7 @@ public class TrafficLightRenderer implements ErrorStripeRenderer {
     text += getMessageByRoots(status.noInspectionRoots, status.rootsNumber, "no.inspections.performed");
     text += HTML_FOOTER;
 
-    text = text.replaceAll(" ", "&nbsp;");
+    text = UIUtil.convertSpace2Nbsp(text);
 
     return text;
   }
index d573711d3b053e9088c3d805485de34903f4e77f..077e49395df9e1a21e1f4fe10d049622ae06da5c 100644 (file)
@@ -65,7 +65,7 @@ public class InspectionDescriptionLinkHandler extends TooltipLinkHandler {
   private void showDescription(final String shortName, final Editor editor, final JEditorPane tooltip) {
     final String description = getDescription(shortName, editor);
     if (description == null) return;
-    final JEditorPane pane = LineTooltipRenderer.initPane(description, new HintHint(tooltip, new Point(0, 0)));
+    final JEditorPane pane = LineTooltipRenderer.initPane(description, new HintHint(tooltip, new Point(0, 0)), editor.getComponent().getRootPane().getLayeredPane());
     pane.select(0, 0);
     pane.setPreferredSize(new Dimension(3 * tooltip.getPreferredSize().width /2, 200));
     final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(pane);
index 20bd127d59bae162a334e530326497cb73949658..dc7ad09ead153079360a2a56e3beba1d3391e142 100644 (file)
@@ -64,20 +64,19 @@ public class LineTooltipRenderer implements TooltipRenderer {
     myText = myText.replaceAll(String.valueOf(UIUtil.MNEMONIC), "");
     final boolean expanded = myCurrentWidth > 0 && dressDescription(editor);
 
-    //pane
-    final JEditorPane pane = initPane(myText, hintHint);
-    pane.setCaretPosition(0);
     final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
     final JComponent contentComponent = editor.getContentComponent();
 
     final JComponent editorComponent = editor.getComponent();
     final JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
 
+    //pane
+    final JEditorPane pane = initPane(myText, hintHint, layeredPane);
+    pane.setCaretPosition(0);
+
     int widthLimit = layeredPane.getWidth() - 10;
     int heightLimit = layeredPane.getHeight() - 5;
 
-    final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(pane);
-    scrollPane.setBorder(null);
     int width = expanded ? 3 * myCurrentWidth / 2 : pane.getPreferredSize().width;
     int height = expanded ? Math.max(pane.getPreferredSize().height, 150) : pane.getPreferredSize().height;
 
@@ -108,13 +107,19 @@ public class LineTooltipRenderer implements TooltipRenderer {
       p.y = 3;
     }
 
-    locateOutsideMouseCursor(editor, layeredPane, p, width, height, heightLimit);
+    if (!hintHint.isAwtTooltip()) {
+      locateOutsideMouseCursor(editor, layeredPane, p, width, height, heightLimit);
+
+      // in order to restrict tooltip size
+      pane.setSize(width, height);
+      pane.setMaximumSize(new Dimension(width, height));
+      pane.setMinimumSize(new Dimension(width, height));
+      pane.setPreferredSize(new Dimension(width, height));
+    }
+
 
-    // in order to restrict tooltip size
-    pane.setSize(width, height);
-    pane.setMaximumSize(new Dimension(width, height));
-    pane.setMinimumSize(new Dimension(width, height));
-    pane.setPreferredSize(new Dimension(width, height));
+    final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(pane);
+    scrollPane.setBorder(null);
 
     scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
     scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
@@ -242,9 +247,15 @@ public class LineTooltipRenderer implements TooltipRenderer {
   protected boolean dressDescription(Editor editor) { return false; }
   protected void stripDescription() {}
 
-  static JEditorPane initPane(@NonNls String text, HintHint hintHint) {
+  static JEditorPane initPane(@NonNls String text, HintHint hintHint, JLayeredPane layeredPane) {
+    final Ref<Dimension> prefSize = new Ref<Dimension>(null);
     text = "<html><head>" + UIUtil.getCssFontDeclaration(hintHint.getTextFont(), hintHint.getTextForeground()) + "</head><body>" + getHtmlBody(text) + "</body></html>";
-    final JEditorPane pane = new JEditorPane(UIUtil.HTML_MIME, text);
+    final JEditorPane pane = new JEditorPane(UIUtil.HTML_MIME, text) {
+      @Override
+      public Dimension getPreferredSize() {
+        return prefSize.get() != null ? prefSize.get() : super.getPreferredSize();
+      }
+    };
     pane.setEditable(false);
 
     if (hintHint.isOwnBorderAllowed()) {
@@ -258,6 +269,20 @@ public class LineTooltipRenderer implements TooltipRenderer {
       pane.setBorder(null);
     }
 
+    if (hintHint.isAwtTooltip()) {
+      Dimension size = layeredPane.getSize();
+      int fitWidth = (int)(size.width * 0.8);
+      Dimension prefSizeOriginal = pane.getPreferredSize();
+      if (prefSizeOriginal.width > fitWidth) {
+        pane.setSize(new Dimension(fitWidth, Integer.MAX_VALUE));
+        Dimension fixedWidthSize = pane.getPreferredSize();
+        prefSize.set(new Dimension(fitWidth, fixedWidthSize.height));
+      } else {
+        prefSize.set(prefSizeOriginal);
+      }
+    }
+
+
     pane.setOpaque(hintHint.isOpaqueAllowed());
     pane.setBackground(hintHint.getTextBackground());
 
index 97161da229ae88702e8f0ea4150cf34fcf9296c2..71cf99e17f9cde146f670ffb4dc30299533e2db3 100644 (file)
@@ -132,6 +132,7 @@ public class TooltipController {
 
   public boolean shouldSurvive(final MouseEvent e) {
     if (myCurrentTooltip != null) {
+      if (myCurrentTooltip.canControlAutoHide()) return true;
       final Point pointOnComponent = new RelativePoint(e).getPointOn(myCurrentTooltip.getComponent()).getPoint();
       final Rectangle bounds = myCurrentTooltip.getBounds();
       if (bounds.x - 10 < pointOnComponent.x && bounds.width + bounds.x + 10 > pointOnComponent.x) {//do not hide hovered tooltip
index 93c6272aa302f92c091eb54fdb7dfbd6ce356b6d..9f269dd568e7eca5f1675d61138b506adb28b5eb 100644 (file)
@@ -69,7 +69,7 @@ public class IdeTooltip {
     return myToCenter;
   }
 
-  protected boolean canAutohideOn(MouseEvent me) {
+  protected boolean canAutohideOn(MouseEvent me, boolean isInsideBalloon) {
     return true;
   }
 
index fbab5a8ee3eb20ef5d19e1774f34819eaadae5b2..1165235e1371fc6f74e28b024c9262040f0f31b3 100644 (file)
@@ -17,12 +17,12 @@ package com.intellij.ide;
 
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.components.ApplicationComponent;
-import com.intellij.openapi.ui.popup.Balloon;
 import com.intellij.openapi.ui.popup.BalloonBuilder;
 import com.intellij.openapi.ui.popup.JBPopupFactory;
 import com.intellij.openapi.util.registry.Registry;
 import com.intellij.openapi.util.registry.RegistryValue;
 import com.intellij.openapi.util.registry.RegistryValueListener;
+import com.intellij.ui.BalloonImpl;
 import com.intellij.ui.awt.RelativePoint;
 import com.intellij.util.Alarm;
 import com.intellij.util.ui.UIUtil;
@@ -40,7 +40,7 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener
   private RegistryValue myIsEnabled;
 
   private Component myCurrentComponent;
-  private Balloon myCurrentTipUi;
+  private BalloonImpl myCurrentTipUi;
   private MouseEvent myCurrentEvent;
   private boolean myCurrentTipIsCentered;
 
@@ -231,7 +231,7 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener
       beforeShow.run();
     }
 
-    myCurrentTipUi = builder.createBalloon();
+    myCurrentTipUi = (BalloonImpl)builder.createBalloon();
     myCurrentComponent = tooltip.getComponent();
     myX = effectivePoint.x;
     myY = effectivePoint.y;
@@ -282,7 +282,9 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener
   }
 
   private boolean hideCurrent(@Nullable MouseEvent me) {
-    if (me != null && myCurrentTooltip != null && !myCurrentTooltip.canAutohideOn(me)) return false;
+    if (me != null && myCurrentTooltip != null && myCurrentTipUi != null) {
+      if (!myCurrentTooltip.canAutohideOn(me, myCurrentTipUi.isInsideBalloon(me))) return false;
+    }
 
     if (myCurrentTipUi != null) {
       myCurrentTipUi.hide();
index 4d390a685da678ddb6be09ab872933c3beb619cd..a3ecb8bf1d3ca6d0095375a7a86f50cd7046eac2 100644 (file)
@@ -136,7 +136,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
     if (highlighters.isEmpty()) return false;
     TooltipRenderer bigRenderer = myTooltipRendererProvider.calcTooltipRenderer(highlighters);
     if (bigRenderer != null) {
-      showTooltip(e, bigRenderer, new HintHint(e));
+      showTooltip(e, bigRenderer, new HintHint(e).setAwtTooltip(true).setPreferredPosition(Balloon.Position.atLeft));
       return true;
     }
     return false;
index 37445240a4d3f57a9fed7d9bb0d3efba385ee2b6..083d253fa2c4305fa96dfc4f56ff5025598f2f5f 100644 (file)
@@ -126,7 +126,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
   private boolean myFadedOut;
   private int myCalloutshift;
 
-  private boolean isInsideBalloon(MouseEvent me) {
+  public boolean isInsideBalloon(MouseEvent me) {
     if (!me.getComponent().isShowing()) return true;
     if (SwingUtilities.isDescendingFrom(me.getComponent(), myComp) || me.getComponent() == myComp) return true;
 
index 30107b0df5ad38f906612c73a683684a662791ff..84757826db711ccdf49fd25e50ea5571f54e3871 100644 (file)
@@ -123,17 +123,20 @@ public class LightweightHint extends UserDataHolderBase implements Hint {
 
 
       if (hintInfo.isAwtTooltip()) {
-        myCurrentIdeTooltip = IdeTooltipManager.getInstance().showTipNow(new IdeTooltip(hintInfo.getOriginalComponent(), hintInfo.getOriginalPoint(), myComponent) {
+        IdeTooltip tooltip = new IdeTooltip(hintInfo.getOriginalComponent(), hintInfo.getOriginalPoint(), myComponent) {
           @Override
-          protected boolean canAutohideOn(MouseEvent me) {
-            return me.getComponent() != hintInfo.getOriginalComponent();
+          protected boolean canAutohideOn(MouseEvent me, boolean isInsideBalloon) {
+            return me.getComponent() != hintInfo.getOriginalComponent() && !isInsideBalloon;
           }
 
           @Override
           protected void onHidden() {
             fireHintHidden();
           }
-        });
+        };
+        tooltip.setPreferredPosition(hintInfo.getPreferredPosition());
+        myComponent.validate();
+        myCurrentIdeTooltip = IdeTooltipManager.getInstance().showTipNow(tooltip);
       } else {
         final Point layeredPanePoint = SwingUtilities.convertPoint(parentComponent, x, y, layeredPane);
         myComponent.setBounds(layeredPanePoint.x, layeredPanePoint.y, preferredSize.width, preferredSize.height);
@@ -287,4 +290,8 @@ public class LightweightHint extends UserDataHolderBase implements Hint {
   public String toString() {
     return getComponent().toString();
   }
+
+  public boolean canControlAutoHide() {
+    return myCurrentIdeTooltip != null;
+  }
 }
index 22c4a2a47fec60af411a8172ecd93cf7706b5268..026bcdb58dfd64fbfd8dfa8de19b32a2f280255b 100644 (file)
@@ -1278,6 +1278,36 @@ public class UIUtil {
     return String.format("<p style=\"margin: 0 %dpx 0 %dpx;\">%s</p>", hPadding, hPadding, html);
   }
 
+  public static String convertSpace2Nbsp(String html) {
+    StringBuffer result = new StringBuffer();
+    int currentPos = 0;
+    int braces = 0;
+    while (currentPos < html.length()) {
+      String each = html.substring(currentPos, currentPos + 1);
+      if ("<".equals(each)) {
+        braces++;
+      } else if (">".equals(each)) {
+        braces--;
+      }
+
+      if (" ".equals(each) && braces == 0) {
+        result.append("&nbsp;");
+      } else {
+        result.append(each);
+      }
+      currentPos++;
+    }
+
+    String text = result.toString();
+    int htmlTag = text.toLowerCase().lastIndexOf("</html>");
+
+    if (htmlTag >= 0) {
+      text = text.substring(0, htmlTag) + "<br><br>" + text.substring(htmlTag);
+    }
+
+    return result.toString();
+  }
+
   public static void invokeLaterIfNeeded(@NotNull Runnable runnable) {
     if (SwingUtilities.isEventDispatchThread()) {
       runnable.run();