void (@NotNull Component owner, int aScreenX, int aScreenY, final boolean considerForcedXY) { if (UiInterceptors.tryIntercept(this)) return; if (ApplicationManager.getApplication() != null && ApplicationManager.getApplication().isHeadlessEnvironment()) return; if (isDisposed()) { throw new IllegalStateException("Popup was already disposed. Recreate a new instance to show again"); } ThreadingAssertions.assertEventDispatchThread(); assert myState == State.INIT : "Popup was already shown. Recreate a new instance to show again."; debugState("show popup", State.INIT); myState = State.SHOWING; installProjectDisposer(); addActivity(); final boolean shouldShow = beforeShow(); if (!shouldShow) { removeActivity(); debugState("rejected to show popup", State.SHOWING); myState = State.INIT; return; } prepareToShow(); installWindowHook(this); Object roundedCornerParams = null; if (WindowRoundedCornersManager.isAvailable()) { PopupCornerType cornerType = getUserData(PopupCornerType.class); if (cornerType == null) { cornerType = PopupCornerType.RoundedWindow; } if (cornerType != PopupCornerType.None) { if ((SystemInfoRt.isMac && myPopupBorderColor != null && UIUtil.isUnderDarcula()) || SystemInfoRt.isWindows) { roundedCornerParams = new Object[]{cornerType, myPopupBorderColor == null ? JBUI.CurrentTheme.Popup.borderColor(true) : myPopupBorderColor}; // must set the border before calculating size below myContent.setBorder(myPopupBorder = PopupBorder.Factory.createEmpty()); } else { roundedCornerParams = cornerType; } } } if (LOG.isDebugEnabled()) { LOG.debug("START calculating bounds for a popup at (" + aScreenX + "," + aScreenY + "," + (considerForcedXY ? "forced" : "not forced") + ") for " + owner); } Dimension sizeToSet = getStoredSize(); if (LOG.isDebugEnabled()) { LOG.debug("The stored size for key " + myDimensionServiceKey + " is " + sizeToSet); } if (myForcedSize != null) { sizeToSet = myForcedSize; if (LOG.isDebugEnabled()) { LOG.debug("Forced the size to " + sizeToSet); } } Rectangle screen = ScreenUtil.getScreenRectangle(aScreenX, aScreenY); if (LOG.isDebugEnabled()) { LOG.debug("The screen rectangle for (" + aScreenX + "," + aScreenY + ") is " + screen); } if (myLocateWithinScreen) { Dimension preferredSize = myContent.getPreferredSize(); if (LOG.isDebugEnabled()) { LOG.debug("The preferred size is " + preferredSize); } Object o = myContent.getClientProperty(FIRST_TIME_SIZE); if (sizeToSet == null && o instanceof Dimension) { if (LOG.isDebugEnabled()) { LOG.debug("The size from the FIRST_TIME_SIZE property is " + o); } int w = ((Dimension)o).width; int h = ((Dimension)o).height; if (w > 0) preferredSize.width = w; if (h > 0) preferredSize.height = h; sizeToSet = preferredSize; if (LOG.isDebugEnabled()) { LOG.debug("The size is set to " + sizeToSet); } } Dimension size = sizeToSet != null ? sizeToSet : preferredSize; if (size.width > screen.width) { size.width = screen.width; sizeToSet = size; if (LOG.isDebugEnabled()) { LOG.debug("Resized to fit the screen width: " + sizeToSet); } } if (size.height > screen.height) { size.height = screen.height; sizeToSet = size; if (LOG.isDebugEnabled()) { LOG.debug("Resized to fit the screen height: " + sizeToSet); } } } if (sizeToSet != null) { Insets insets = myContent.getInsets(); JBInsets.addTo(sizeToSet, insets); if (LOG.isDebugEnabled()) { LOG.debug("Added insets (" + insets + "), the size is now: " + sizeToSet); } Dimension minimumSize = myContent.getMinimumSize(); sizeToSet.width = Math.max(sizeToSet.width, minimumSize.width); sizeToSet.height = Math.max(sizeToSet.height, minimumSize.height); if (LOG.isDebugEnabled()) { LOG.debug("Coerced to the minimum size (" + minimumSize + "): " + sizeToSet); } myContent.setSize(sizeToSet); myContent.setPreferredSize(sizeToSet); } Point xy = new Point(aScreenX, aScreenY); boolean adjustXY = true; if (myUseDimServiceForXYLocation) { Point storedLocation = getStoredLocation(); if (LOG.isDebugEnabled()) { LOG.debug("The stored location for key " + myDimensionServiceKey + " is " + storedLocation); } if (storedLocation != null) { xy = storedLocation; adjustXY = false; } } if (adjustXY) { final Insets insets = myContent.getInsets(); if (insets != null) { xy.x -= insets.left; xy.y -= insets.top; if (LOG.isDebugEnabled()) { LOG.debug("Location after adjusting by insets (" + insets + ") is " + xy); } } } if (considerForcedXY && myForcedLocation != null) { xy = myForcedLocation; if (LOG.isDebugEnabled()) { LOG.debug("Location forced to " + myForcedLocation); } } fixLocateByContent(xy, false); if (LOG.isDebugEnabled()) { LOG.debug("Location after fixing by content is " + xy); } Rectangle targetBounds = new Rectangle(xy, myContent.getPreferredSize()); if (LOG.isDebugEnabled()) { LOG.debug("Target bounds " + targetBounds); } if (targetBounds.width > screen.width || targetBounds.height > screen.height) { StringBuilder sb = new StringBuilder("popup preferred size is bigger than screen: "); sb.append(targetBounds.width).append("x").append(targetBounds.height); IJSwingUtilities.appendComponentClassNames(sb, myContent); LOG.warn(sb.toString()); } Rectangle original = new Rectangle(targetBounds); if (myLocateWithinScreen) { ScreenUtil.moveToFit(targetBounds, screen, null); if (LOG.isDebugEnabled()) { LOG.debug("Target bounds after moving to fit the screen: " + targetBounds); } } else { //even when LocateWithinScreen option is disabled, popup should not be shown in invisible area fitToVisibleArea(targetBounds); if (LOG.isDebugEnabled()) { LOG.debug("Target bounds after moving to fit the visible area: " + targetBounds); } } if (LOG.isDebugEnabled()) { LOG.debug("END calculating popup bounds, the result is " + targetBounds); } if (myMouseOutCanceller != null) { myMouseOutCanceller.myEverEntered = targetBounds.equals(original); } // prevent hiding of a floating toolbar Point pointOnOwner = new Point(aScreenX, aScreenY); SwingUtilities.convertPointFromScreen(pointOnOwner, owner); if (ActionToolbarImpl.isInPopupToolbar(SwingUtilities.getDeepestComponentAt(owner, pointOnOwner.x, pointOnOwner.y))) { AutoPopupSupportingListener.installOn(this); } myOwner = getFrameOrDialog(owner); // use correct popup owner for non-modal dialogs too if (myOwner == null) { myOwner = owner; } myRequestorComponent = owner; myPopupType = getMostSuitablePopupType(); myNativePopup = myPopupType != PopupComponentFactory.PopupType.DIALOG; Component popupOwner = myOwner; if (popupOwner instanceof RootPaneContainer && !(popupOwner instanceof IdeFrame && !Registry.is("popup.fix.ide.frame.owner"))) { // JDK uses cached heavyweight popup for a window ancestor RootPaneContainer root = (RootPaneContainer)popupOwner; popupOwner = root.getRootPane(); LOG.debug("popup owner fixed for JDK cache"); } if (LOG.isDebugEnabled()) { LOG.debug("expected preferred size: " + myContent.getPreferredSize()); } PopupComponentFactory factory = PopupComponentFactory.getCurrentInstance(); myPopup = factory.createPopupComponent(myPopupType, popupOwner, myContent, targetBounds.x, targetBounds.y, this); if (LOG.isDebugEnabled()) { LOG.debug("START adjusting popup bounds after creation"); LOG.debug(" actual preferred size: " + myContent.getPreferredSize()); } if (targetBounds.width != myContent.getWidth() || targetBounds.height != myContent.getHeight()) { // JDK uses cached heavyweight popup that is not initialized properly LOG.debug("the expected size (" + targetBounds.getSize() + ") is not equal to the actual size (" + myContent.getSize() + ")"); Window popup = myPopup.getWindow(); if (popup != null) { popup.setSize(targetBounds.width, targetBounds.height); if (myContent.getParent().getComponentCount() != 1) { LOG.debug("unexpected count of components in heavy-weight popup"); } } else { LOG.debug("cannot fix size for non-heavy-weight popup because its window is null"); } } if (myResizable) { final JRootPane root = myContent.getRootPane(); final IdeGlassPaneImpl glass = new IdeGlassPaneImpl(root); root.setGlassPane(glass); WindowResizeListenerEx resizeListener = new WindowResizeListenerEx( glass, myComponent, myMovable ? JBUI.insets(4) : JBUI.insets(0, 0, 4, 4), null); resizeListener.install(this); resizeListener.addResizeListeners(() -> { myResizeListeners.forEach(Runnable::run); }); myResizeListener = resizeListener; } setIsMovable(myMovable); notifyListeners(); myPopup.setRequestFocus(myRequestFocus); final Window window = getContentWindow(myContent); if (window instanceof IdeFrame) { LOG.warn("Lightweight popup is shown using AbstractPopup class. But this class is not supposed to work with lightweight popups."); } window.setFocusableWindowState(myRequestFocus); window.setFocusable(myRequestFocus); // Swing popup default always on top state is set in true window.setAlwaysOnTop(false); if (myFocusable) { FocusTraversalPolicy focusTraversalPolicy = new FocusTraversalPolicy() { @Override public Component getComponentAfter(Container aContainer, Component aComponent) { return getComponent(); } private Component getComponent() { return myPreferredFocusedComponent == null ? myComponent : myPreferredFocusedComponent; } @Override public Component getComponentBefore(Container aContainer, Component aComponent) { return getComponent(); } @Override public Component getFirstComponent(Container aContainer) { return getComponent(); } @Override public Component getLastComponent(Container aContainer) { return getComponent(); } @Override public Component getDefaultComponent(Container aContainer) { return getComponent(); } }; window.setFocusTraversalPolicy(focusTraversalPolicy); Disposer.register(this, () -> window.setFocusTraversalPolicy(null)); } window.setAutoRequestFocus(myRequestFocus); if (roundedCornerParams != null) { WindowRoundedCornersManager.setRoundedCorners(window, roundedCornerParams); } if (myNormalWindowLevel && !window.isDisplayable()) { window.setType(Window.Type.NORMAL); } myWindow = window; if (LOG.isDebugEnabled()) { LOG.debug("Setting minimum size to " + myMinSize); } setMinimumSize(myMinSize); if (LOG.isDebugEnabled()) { LOG.debug("Minimum size set to " + window.getMinimumSize()); } TouchbarSupport.showPopupItems(this, myContent); modalEntitiesWhenShown = LaterInvocator.getCurrentModalEntities(); myPopup.show(); Rectangle bounds = window.getBounds(); if (LOG.isDebugEnabled()) { LOG.debug("END adjusting popup bounds after creation, the result is: " + bounds + ", starting final post-show adjustments"); } PopupLocationTracker.register(this); if (bounds.width > screen.width || bounds.height > screen.height) { if (LOG.isDebugEnabled()) { LOG.debug("Bounds won't fit into the screen, adjusting"); } ScreenUtil.fitToScreen(bounds); window.setBounds(bounds); } if (LOG.isDebugEnabled()) { GraphicsDevice device = ScreenUtil.getScreenDevice(bounds); StringBuilder sb = new StringBuilder("Popup is shown with bounds " + bounds); if (device != null) sb.append(" on screen with ID \"").append(device.getIDstring()).append("\""); LOG.debug(sb.toString()); } WindowAction.setEnabledFor(myPopup.getWindow(), myResizable); myWindowListener = new MyWindowListener(); window.addWindowListener(myWindowListener); if (myWindow != null) { // dialog wrapper-based popups do this internally through peer, // for other popups like jdialog-based we should exclude them manually, but // we still have to be able to use IdeFrame as parent if (!myMayBeParent && !(myWindow instanceof Frame)) { WindowManager.getInstance().doNotSuggestAsParent(myWindow); } } final Runnable afterShow = () -> { if (isDisposed()) { LOG.debug("popup is disposed after showing"); removeActivity(); return; } if ((myPreferredFocusedComponent instanceof AbstractButton || myPreferredFocusedComponent instanceof JTextField) && myFocusable) { IJSwingUtilities.moveMousePointerOn(myPreferredFocusedComponent); } removeActivity(); afterShow(); }; if (myRequestFocus) { if (myPreferredFocusedComponent != null) { myPreferredFocusedComponent.requestFocus(); } else { _requestFocus(); } window.setAutoRequestFocus(myRequestFocus); SwingUtilities.invokeLater(afterShow); } else { //noinspection SSBasedInspection SwingUtilities.invokeLater(() -> { if (isDisposed()) { removeActivity(); return; } afterShow.run(); }); } debugState("popup shown", State.SHOWING); myState = State.SHOWN; myOpeningTime = System.currentTimeMillis(); afterShowSync(); }