private static void showParameterHint( final PsiElement element, final Editor editor, final Object[] descriptors, final Project project, @Nullable PsiElement highlighted, final int elementStart, final ParameterInfoHandler handler, final boolean requestFocus) { if (ParameterInfoController.isAlreadyShown(editor, elementStart)) return; if (editor.isDisposed() || !editor.getComponent().isVisible()) return; final ParameterInfoComponent component = new ParameterInfoComponent(descriptors, editor, handler, requestFocus); component.setParameterOwner(element); component.setRequestFocus(requestFocus); if (highlighted != null) { component.setHighlightedParameter(highlighted); } component.update(); // to have correct preferred size final LightweightHint hint = new LightweightHint(component); hint.setSelectingHint(true); final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl(); final ShowParameterInfoHandler.BestLocationPointProvider provider = new MyBestLocationPointProvider(editor); final Pair<Point, Short> pos = provider.getBestPointPosition(hint, element, elementStart, true, HintManager.UNDER); PsiDocumentManager.getInstance(project) .performLaterWhenAllCommitted( () -> { if (editor.isDisposed() || DumbService.isDumb(project)) return; final Document document = editor.getDocument(); if (document.getTextLength() < elementStart) return; HintHint hintHint = HintManagerImpl.createHintHint(editor, pos.getFirst(), hint, pos.getSecond()); hintHint.setExplicitClose(true); hintHint.setRequestFocus(requestFocus); Editor editorToShow = editor instanceof EditorWindow ? ((EditorWindow) editor).getDelegate() : editor; // is case of injection we need to calculate position for EditorWindow // also we need to show the hint in the main editor because of intention bulb hintManager.showEditorHint( hint, editorToShow, pos.getFirst(), HintManager.HIDE_BY_ESCAPE | HintManager.UPDATE_BY_SCROLLING, 0, false, hintHint); new ParameterInfoController(project, editor, elementStart, hint, handler, provider); }); }
/** @return Point in layered pane coordinate system */ static Pair<Point, Short> chooseBestHintPosition( Project project, Editor editor, int line, int col, LightweightHint hint, boolean awtTooltip, short preferredPosition) { HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl(); Dimension hintSize = hint.getComponent().getPreferredSize(); JComponent editorComponent = editor.getComponent(); JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane(); Point p1; Point p2; boolean isLookupShown = LookupManager.getInstance(project).getActiveLookup() != null; if (isLookupShown) { p1 = hintManager.getHintPosition(hint, editor, HintManager.UNDER); p2 = hintManager.getHintPosition(hint, editor, HintManager.ABOVE); } else { LogicalPosition pos = new LogicalPosition(line, col); p1 = HintManagerImpl.getHintPosition(hint, editor, pos, HintManager.UNDER); p2 = HintManagerImpl.getHintPosition(hint, editor, pos, HintManager.ABOVE); } if (!awtTooltip) { p1.x = Math.min(p1.x, layeredPane.getWidth() - hintSize.width); p1.x = Math.max(p1.x, 0); p2.x = Math.min(p2.x, layeredPane.getWidth() - hintSize.width); p2.x = Math.max(p2.x, 0); } boolean p1Ok = p1.y + hintSize.height < layeredPane.getHeight(); boolean p2Ok = p2.y >= 0; if (isLookupShown) { if (p1Ok) return new Pair<Point, Short>(p1, HintManager.UNDER); if (p2Ok) return new Pair<Point, Short>(p2, HintManager.ABOVE); } else { if (preferredPosition != HintManager.DEFAULT) { if (preferredPosition == HintManager.ABOVE) { if (p2Ok) return new Pair<Point, Short>(p2, HintManager.ABOVE); } else if (preferredPosition == HintManager.UNDER) { if (p1Ok) return new Pair<Point, Short>(p1, HintManager.UNDER); } } if (p1Ok) return new Pair<Point, Short>(p1, HintManager.UNDER); if (p2Ok) return new Pair<Point, Short>(p2, HintManager.ABOVE); } int underSpace = layeredPane.getHeight() - p1.y; int aboveSpace = p2.y; return aboveSpace > underSpace ? new Pair<Point, Short>(new Point(p2.x, 0), HintManager.UNDER) : new Pair<Point, Short>(p1, HintManager.ABOVE); }
private void updateComponent() { if (!myHint.isVisible()) { Disposer.dispose(this); return; } final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(myEditor.getDocument()); CharSequence chars = myEditor.getDocument().getCharsSequence(); boolean noDelimiter = myHandler instanceof ParameterInfoHandlerWithTabActionSupport && ((ParameterInfoHandlerWithTabActionSupport) myHandler) .getActualParameterDelimiterType() == TokenType.WHITE_SPACE; int caretOffset = myEditor.getCaretModel().getOffset(); final int offset = noDelimiter ? caretOffset : CharArrayUtil.shiftBackward(chars, caretOffset - 1, " \t") + 1; final UpdateParameterInfoContext context = new MyUpdateParameterInfoContext(offset, file); final Object elementForUpdating = myHandler.findElementForUpdatingParameterInfo(context); if (elementForUpdating != null) { myHandler.updateParameterInfo(elementForUpdating, context); if (!myDisposed && myHint.isVisible() && !myEditor.isDisposed() && myEditor.getComponent().getRootPane() != null) { myComponent.update(); IdeTooltip tooltip = myHint.getCurrentIdeTooltip(); short position = tooltip != null ? toShort(tooltip.getPreferredPosition()) : HintManager.UNDER; Pair<Point, Short> pos = myProvider.getBestPointPosition( myHint, elementForUpdating instanceof PsiElement ? (PsiElement) elementForUpdating : null, caretOffset, true, position); HintManagerImpl.adjustEditorHintPosition(myHint, myEditor, pos.getFirst(), pos.getSecond()); } } else { context.removeHint(); } }
@Override @NotNull public Pair<Point, Short> getBestPointPosition( LightweightHint hint, final PsiElement list, int offset, final boolean awtTooltip, short preferredPosition) { if (list != null) { TextRange range = list.getTextRange(); if (!range.contains(offset)) { offset = range.getStartOffset() + 1; } } if (previousOffset == offset) return Pair.create(previousBestPoint, previousBestPosition); final boolean isMultiline = list != null && StringUtil.containsAnyChar(list.getText(), "\n\r"); final LogicalPosition pos = myEditor.offsetToLogicalPosition(offset); Pair<Point, Short> position; if (!isMultiline) { position = chooseBestHintPosition( myEditor.getProject(), myEditor, pos.line, pos.column, hint, awtTooltip, preferredPosition); } else { Point p = HintManagerImpl.getHintPosition(hint, myEditor, pos, HintManager.ABOVE); position = new Pair<Point, Short>(p, HintManager.ABOVE); } previousBestPoint = position.getFirst(); previousBestPosition = position.getSecond(); previousOffset = offset; return position; }
/** * @param hideByAnyKey * @param x <code>x</code> coordinate in layered pane coordinate system. * @param y <code>y</code> coordinate in layered pane coordinate system. */ @Nullable public static LightweightHint showEditorFragmentHintAt( Editor editor, TextRange range, int x, int y, boolean showUpward, boolean showFolding, boolean hideByAnyKey) { if (ApplicationManager.getApplication().isUnitTestMode()) return null; Document document = editor.getDocument(); int startOffset = range.getStartOffset(); int startLine = document.getLineNumber(startOffset); CharSequence text = document.getCharsSequence(); // There is a possible case that we have a situation like below: // line 1 // line 2 <fragment start> // line 3<fragment end> // We don't want to include 'line 2' to the target fragment then. boolean incrementLine = false; for (int offset = startOffset, max = Math.min(range.getEndOffset(), text.length()); offset < max; offset++) { char c = text.charAt(offset); incrementLine = StringUtil.isWhiteSpace(c); if (!incrementLine || c == '\n') { break; } } if (incrementLine) { startLine++; } int endLine = Math.min(document.getLineNumber(range.getEndOffset()) + 1, document.getLineCount() - 1); // if (editor.logicalPositionToXY(new LogicalPosition(startLine, 0)).y >= // editor.logicalPositionToXY(new LogicalPosition(endLine, 0)).y) return null; if (startLine >= endLine) return null; EditorFragmentComponent fragmentComponent = createEditorFragmentComponent(editor, startLine, endLine, showFolding, true); if (showUpward) { y -= fragmentComponent.getPreferredSize().height + 10; y = Math.max(0, y); } final JComponent c = editor.getComponent(); x = SwingUtilities.convertPoint(c, new Point(-3, 0), UIUtil.getRootPane(c)).x; // IDEA-68016 Point p = new Point(x, y); LightweightHint hint = new MyComponentHint(fragmentComponent); HintManagerImpl.getInstanceImpl() .showEditorHint( hint, editor, p, (hideByAnyKey ? HintManager.HIDE_BY_ANY_KEY : 0) | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_MOUSEOVER, 0, false, new HintHint(editor, p)); return hint; }