void (PositionTracker<Balloon> tracker, AbstractPosition position) { assert !isDisposed : "Balloon is already disposed"; if (isVisible()) return; final Component comp = tracker.getComponent(); if (!comp.isShowing()) return; myTracker = tracker; myTracker.init(this); JRootPane root = Objects.requireNonNull(UIUtil.getRootPane(comp)); myVisible = true; myLayeredPane = root.getLayeredPane(); myPosition = position; UIUtil.setFutureRootPane(myContent, root); myFocusManager = IdeFocusManager.findInstanceByComponent(myLayeredPane); final Ref<Component> originalFocusOwner = new Ref<>(); final Ref<ActionCallback> proxyFocusRequest = new Ref<>(ActionCallback.DONE); boolean mnemonicsFix = myDialogMode && SystemInfo.isMac && Registry.is("ide.mac.inplaceDialogMnemonicsFix"); if (mnemonicsFix) { proxyFocusRequest.set(new ActionCallback()); myFocusManager.doWhenFocusSettlesDown(new ExpirableRunnable() { @Override public boolean isExpired() { return isDisposed(); } @Override public void run() { IdeEventQueue.getInstance().disableInputMethods(BalloonImpl.this); originalFocusOwner.set(myFocusManager.getFocusOwner()); } }); } myLayeredPane.addComponentListener(myComponentListener); myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(myLayeredPane), myCalloutShift); if (isDisposed) return; //tracker may dispose the balloon int positionChangeFix = 0; if (myShowPointer) { Rectangle rec = getRecForPosition(myPosition, true); JBInsets.removeFrom(rec, getShadowBorderInsets()); if (!myPosition.isOkToHavePointer(myTargetPoint, rec, getPointerLength(myPosition), getPointerWidth(myPosition), getArc())) { rec = getRecForPosition(myPosition, false); Rectangle lp = new Rectangle(new Point(myContainerInsets.left, myContainerInsets.top), myLayeredPane.getSize()); lp.width -= myContainerInsets.right; lp.height -= myContainerInsets.bottom; if (!lp.contains(rec) || !PopupLocationTracker.canRectangleBeUsed(myLayeredPane, rec, this)) { Rectangle2D currentSquare = lp.createIntersection(rec); double maxSquare = currentSquare.getWidth() * currentSquare.getHeight(); AbstractPosition targetPosition = myPosition; for (AbstractPosition eachPosition : myPosition.getOtherPositions()) { Rectangle2D eachIntersection = lp.createIntersection(getRecForPosition(eachPosition, false)); double eachSquare = eachIntersection.getWidth() * eachIntersection.getHeight(); if (maxSquare < eachSquare) { maxSquare = eachSquare; targetPosition = eachPosition; } } myPosition = targetPosition; positionChangeFix = myPosition.getChangeShift(position, myPositionChangeXShift, myPositionChangeYShift); } } } if (myPosition != position) { myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(myLayeredPane), myCalloutShift > 0 ? myCalloutShift + positionChangeFix : positionChangeFix); position = myPosition; } createComponent(); Rectangle r = getRecForPosition(myPosition, false); Point location = r.getLocation(); SwingUtilities.convertPointToScreen(location, myLayeredPane); r.setLocation(location); if (!PopupLocationTracker.canRectangleBeUsed(myLayeredPane, r, this)) { for (AbstractPosition eachPosition : myPosition.getOtherPositions()) { r = getRecForPosition(eachPosition, false); location = r.getLocation(); SwingUtilities.convertPointToScreen(location, myLayeredPane); r.setLocation(location); if (PopupLocationTracker.canRectangleBeUsed(myLayeredPane, r, this)) { myPosition = eachPosition; positionChangeFix = myPosition.getChangeShift(position, myPositionChangeXShift, myPositionChangeYShift); myTargetPoint = myPosition.getShiftedPoint(myTracker.recalculateLocation(this).getPoint(myLayeredPane), myCalloutShift > 0 ? myCalloutShift + positionChangeFix : positionChangeFix); myPosition.updateBounds(this); break; } } } component.validate(); Rectangle rec = component.getContentBounds(); if (myShowPointer && !myPosition.isOkToHavePointer(myTargetPoint, rec, getPointerLength(myPosition), getPointerWidth(myPosition), getArc())) { myShowPointer = false; component.removeAll(); myLayeredPane.remove(component); createComponent(); Dimension availSpace = myLayeredPane.getSize(); Dimension reqSpace = component.getSize(); if (!new Rectangle(availSpace).contains(new Rectangle(reqSpace))) { // Balloon is bigger than window, don't show it at all. LOG.warn("Not enough space to show: " + "required [" + reqSpace.width + " x " + reqSpace.height + "], " + "available [" + availSpace.width + " x " + availSpace.height + "]"); component.removeAll(); myLayeredPane.remove(component); myLayeredPane = null; hide(); return; } } for (JBPopupListener each : myListeners) { each.beforeShown(new LightweightWindowEvent(this)); } if (isAnimationEnabled()) { runAnimation(true, myLayeredPane, null); } myLayeredPane.revalidate(); myLayeredPane.repaint(); if (myRequestFocus) { myFocusManager.doWhenFocusSettlesDown(new ExpirableRunnable() { @Override public boolean isExpired() { return isDisposed(); } @Override public void run() { myOriginalFocusOwner = myFocusManager.getFocusOwner(); // Set the accessible parent so that screen readers don't announce // a window context change -- the tooltip is "logically" hosted // inside the component (e.g. editor) it appears on top of. AccessibleContextUtil.setParent(myContent, myOriginalFocusOwner); // Set the focus to "myContent" myFocusManager.requestFocus(getContentToFocus(), true); } }); } if (mnemonicsFix) { proxyFocusRequest.get().doWhenDone(() -> myFocusManager.requestFocus(originalFocusOwner.get(), true)); } Toolkit.getDefaultToolkit().addAWTEventListener( myAwtActivityListener, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.KEY_EVENT_MASK); if (ApplicationManager.getApplication() != null) { ApplicationManager.getApplication().getMessageBus().connect(this).subscribe(AnActionListener.TOPIC, new AnActionListener() { @Override public void beforeActionPerformed(@NotNull AnAction action, @NotNull AnActionEvent event) { if (myHideOnAction && !(action instanceof HintManagerImpl.ActionToIgnore)) { hide(); } } }); } if (myHideOnLinkClick) { JEditorPane editorPane = UIUtil.uiTraverser(myContent).traverse().filter(JEditorPane.class).first(); if (editorPane != null) { editorPane.addHyperlinkListener(new HyperlinkAdapter() { @Override protected void hyperlinkActivated(@NotNull HyperlinkEvent e) { hide(); } }); } } if (myId != null) { BalloonUsageCollector.BALLOON_SHOWN.log(myId); } getBalloonListener().balloonShown(this); }