diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java index 25d09df49dde..c37f7840faa2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java @@ -71,6 +71,7 @@ import com.intellij.psi.search.searches.DefinitionsSearch; import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.HintListener; import com.intellij.ui.LightweightHint; +import com.intellij.ui.ScreenUtil; import com.intellij.usageView.UsageViewShortNameLocation; import com.intellij.usageView.UsageViewTypeLocation; import com.intellij.util.Alarm; @@ -168,7 +169,8 @@ public class CtrlMouseHandler extends AbstractProjectComponent { } MouseEvent mouseEvent = e.getMouseEvent(); - if (isMouseOverTooltip(mouseEvent.getLocationOnScreen()) || isMouseMovedTowardTooltip(mouseEvent.getLocationOnScreen())) { + if (isMouseOverTooltip(mouseEvent.getLocationOnScreen()) + || ScreenUtil.isMovementTowards(myPrevMouseLocation, mouseEvent.getLocationOnScreen(), getHintBounds())) { myPrevMouseLocation = mouseEvent.getLocationOnScreen(); return; } @@ -247,63 +249,6 @@ public class CtrlMouseHandler extends AbstractProjectComponent { return bounds != null && bounds.contains(mouseLocationOnScreen); } - private boolean isMouseMovedTowardTooltip(@NotNull Point mouseLocationOnScreen) { - Rectangle bounds = getHintBounds(); - if (bounds == null) { - return false; - } - - Point prevLocation = myPrevMouseLocation; - if (prevLocation == null) { - myPrevMouseLocation = mouseLocationOnScreen; - return true; - } - else if (prevLocation.equals(mouseLocationOnScreen)) { - return true; - } - - int dx = prevLocation.x - mouseLocationOnScreen.x; - int dy = prevLocation.y - mouseLocationOnScreen.y; - - // Check if the mouse goes out of the control. - if (dx > 0 && bounds.x >= prevLocation.x) return false; - if (dx < 0 && bounds.x + bounds.width <= prevLocation.x) return false; - if (dy > 0 && bounds.y + bounds.height >= prevLocation.y) return false; - if (dy < 0 && bounds.y <= prevLocation.y) return false; - if (dx == 0) { - return (mouseLocationOnScreen.x >= bounds.x && mouseLocationOnScreen.x < bounds.x + bounds.width) - && (dy > 0 ^ bounds.y > mouseLocationOnScreen.y); - } - if (dy == 0) { - return (mouseLocationOnScreen.y >= bounds.y && mouseLocationOnScreen.y < bounds.y + bounds.height) - && (dx > 0 ^ bounds.x > mouseLocationOnScreen.x); - } - - - // Calculate line equation parameters - y = a * x + b - float a = (float)dy / dx; - float b = mouseLocationOnScreen.y - a * mouseLocationOnScreen.x; - - // Check if crossing point with any tooltip border line is within bounds. Don't bother with floating point inaccuracy here. - - // Left border. - float crossY = a * bounds.x + b; - if (crossY >= bounds.y && crossY < bounds.y + bounds.height) return true; - - // Right border. - crossY = a * (bounds.x + bounds.width) + b; - if (crossY >= bounds.y && crossY < bounds.y + bounds.height) return true; - - // Top border. - float crossX = (bounds.y - b) / a; - if (crossX >= bounds.x && crossX < bounds.x + bounds.width) return true; - - // Bottom border - crossX = (bounds.y + bounds.height - b) / a; - if (crossX >= bounds.x && crossX < bounds.x + bounds.width) return true; - return false; - } - @Nullable private Rectangle getHintBounds() { LightweightHint hint = myHint; diff --git a/platform/platform-api/src/com/intellij/ui/ScreenUtil.java b/platform/platform-api/src/com/intellij/ui/ScreenUtil.java index 2ded3ab17f65..ade5f47d9b26 100644 --- a/platform/platform-api/src/com/intellij/ui/ScreenUtil.java +++ b/platform/platform-api/src/com/intellij/ui/ScreenUtil.java @@ -234,4 +234,62 @@ public class ScreenUtil { rect.y = (int)screen.getMinY(); } } + + /** + * + * @param prevLocation - previous location on screen + * @param location - current location on screen + * @param bounds - area to check if location shifted towards or not. Also in screen coordinates + * @return true if movement from prevLocation to location is towards specified rectangular area + */ + public static boolean isMovementTowards(final Point prevLocation, final Point location, final Rectangle bounds) { + if (bounds == null) { + return false; + } + if (prevLocation == null || prevLocation.equals(location)) { + return true; + } + + int dx = prevLocation.x - location.x; + int dy = prevLocation.y - location.y; + + // Check if the mouse goes out of the control. + if (dx > 0 && bounds.x >= prevLocation.x) return false; + if (dx < 0 && bounds.x + bounds.width <= prevLocation.x) return false; + if (dy > 0 && bounds.y + bounds.height >= prevLocation.y) return false; + if (dy < 0 && bounds.y <= prevLocation.y) return false; + if (dx == 0) { + return (location.x >= bounds.x && location.x < bounds.x + bounds.width) + && (dy > 0 ^ bounds.y > location.y); + } + if (dy == 0) { + return (location.y >= bounds.y && location.y < bounds.y + bounds.height) + && (dx > 0 ^ bounds.x > location.x); + } + + + // Calculate line equation parameters - y = a * x + b + float a = (float)dy / dx; + float b = location.y - a * location.x; + + // Check if crossing point with any tooltip border line is within bounds. Don't bother with floating point inaccuracy here. + + // Left border. + float crossY = a * bounds.x + b; + if (crossY >= bounds.y && crossY < bounds.y + bounds.height) return true; + + // Right border. + crossY = a * (bounds.x + bounds.width) + b; + if (crossY >= bounds.y && crossY < bounds.y + bounds.height) return true; + + // Top border. + float crossX = (bounds.y - b) / a; + if (crossX >= bounds.x && crossX < bounds.x + bounds.width) return true; + + // Bottom border + crossX = (bounds.y + bounds.height - b) / a; + if (crossX >= bounds.x && crossX < bounds.x + bounds.width) return true; + + return false; + } } diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index 388f96ceea77..a1381ddf0534 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -24,6 +24,7 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.ex.AnActionListener; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.keymap.impl.IdeMouseEventDispatcher; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.GraphicsConfig; import com.intellij.openapi.ui.MessageType; @@ -164,6 +165,7 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { private boolean myShadow = false; private Layer myLayer; private boolean myBlockClicks; + private Point myPrevMousePoint = null; public boolean isInsideBalloon(MouseEvent me) { return isInside(new RelativePoint(me)); @@ -177,9 +179,15 @@ public class BalloonImpl implements Balloon, IdeTooltip.Ui { if (cmp == myCloseRec) return true; if (SwingUtilities.isDescendingFrom(cmp, myComp) || cmp == myComp) return true; if (myComp == null || !myComp.isShowing()) return false; - if (new Rectangle(myComp.getLocationOnScreen(), myComp.getSize()).contains(target.getScreenPoint())) return true; + Rectangle rectangleOnScreen = new Rectangle(myComp.getLocationOnScreen(), myComp.getSize()); + if (rectangleOnScreen.contains(target.getScreenPoint())) return true; - return false; + try { + return ScreenUtil.isMovementTowards(myPrevMousePoint, target.getScreenPoint(), rectangleOnScreen); + } + finally { + myPrevMousePoint = target.getScreenPoint(); + } } private final ComponentAdapter myComponentListener = new ComponentAdapter() {