mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-66333 Quick documentation lookup on mouse hover
1. Generalize hint component access for both 'popup' and 'layered pane' display modes; 2. Don't consider pure horizontal/vertical mouse moves as a 'move toward tooltip'; 3. Corrected 'show documentation for element under caret' processing for the docked documentation window; 4. Refactoring and green code policy;
This commit is contained in:
+5
-4
@@ -47,7 +47,8 @@ import java.awt.*;
|
||||
*/
|
||||
public abstract class DockablePopupManager<T extends JComponent & Disposable> {
|
||||
protected ToolWindow myToolWindow = null;
|
||||
protected boolean myAutoUpdateDocumentation = PropertiesComponent.getInstance().isTrueValue(getAutoUpdateEnabledProperty());
|
||||
protected boolean myDefaultAutoUpdateDocumentation = PropertiesComponent.getInstance().isTrueValue(getAutoUpdateEnabledProperty());
|
||||
protected boolean myAutoUpdateDocumentation = myDefaultAutoUpdateDocumentation;
|
||||
protected Runnable myAutoUpdateRequest;
|
||||
@NotNull protected final Project myProject;
|
||||
|
||||
@@ -105,7 +106,7 @@ public abstract class DockablePopupManager<T extends JComponent & Disposable> {
|
||||
|
||||
new UiNotifyConnector(component, new Activatable() {
|
||||
public void showNotify() {
|
||||
restartAutoUpdate(myAutoUpdateDocumentation);
|
||||
restartAutoUpdate(myDefaultAutoUpdateDocumentation);
|
||||
}
|
||||
|
||||
public void hideNotify() {
|
||||
@@ -145,12 +146,12 @@ public abstract class DockablePopupManager<T extends JComponent & Disposable> {
|
||||
}
|
||||
|
||||
|
||||
private void restartAutoUpdate(final boolean state) {
|
||||
protected void restartAutoUpdate(final boolean state) {
|
||||
if (state && myToolWindow != null) {
|
||||
if (myAutoUpdateRequest == null) {
|
||||
myAutoUpdateRequest = new Runnable() {
|
||||
public void run() {
|
||||
if (!myAutoUpdateDocumentation || myProject.isDisposed()) return;
|
||||
if (myProject.isDisposed()) return;
|
||||
|
||||
AsyncResult<DataContext> asyncResult = DataManager.getInstance().getDataContextFromFocus();
|
||||
DataContext dataContext = asyncResult.getResult();
|
||||
|
||||
+1
-1
@@ -174,7 +174,7 @@ public class DocumentationManager extends DockablePopupManager<DocumentationComp
|
||||
}
|
||||
|
||||
public void setAllowContentUpdateFromContext(boolean allow) {
|
||||
myAutoUpdateDocumentation = allow;
|
||||
restartAutoUpdate(allow);
|
||||
}
|
||||
|
||||
public void showJavaDocInfoAtToolWindow(@NotNull PsiElement element, @NotNull PsiElement original) {
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2000-2012 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInsight.navigation;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.PatchedWeakReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Expands {@link AnAction} contract for documentation-related actions that may be called from the IDE tooltip.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/26/12 12:28 PM
|
||||
*/
|
||||
public abstract class AbstractDocumentationTooltipAction extends AnAction {
|
||||
|
||||
@Nullable private WeakReference<PsiElement> myDocAnchor;
|
||||
@Nullable private WeakReference<PsiElement> myOriginalElement;
|
||||
|
||||
public void setDocInfo(@NotNull PsiElement docAnchor, @NotNull PsiElement originalElement) {
|
||||
myDocAnchor = new PatchedWeakReference<PsiElement>(docAnchor);
|
||||
myOriginalElement = new PatchedWeakReference<PsiElement>(originalElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
e.getPresentation().setVisible(getDocInfo() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
Pair<PsiElement, PsiElement> info = getDocInfo();
|
||||
if (info == null) {
|
||||
return;
|
||||
}
|
||||
doActionPerformed(e.getDataContext(), info.first, info.second);
|
||||
myDocAnchor = null;
|
||||
myOriginalElement = null;
|
||||
}
|
||||
|
||||
protected abstract void doActionPerformed(@NotNull DataContext context,
|
||||
@NotNull PsiElement docAnchor,
|
||||
@NotNull PsiElement originalElement);
|
||||
|
||||
@Nullable
|
||||
private Pair<PsiElement/* doc anchor */, PsiElement /* original element */> getDocInfo() {
|
||||
WeakReference<PsiElement> docAnchorRef = myDocAnchor;
|
||||
if (docAnchorRef == null) {
|
||||
return null;
|
||||
}
|
||||
PsiElement docAnchor = docAnchorRef.get();
|
||||
if (docAnchor == null) {
|
||||
return null;
|
||||
}
|
||||
WeakReference<PsiElement> originalElementRef = myOriginalElement;
|
||||
if (originalElementRef == null) {
|
||||
return null;
|
||||
}
|
||||
PsiElement originalElement = originalElementRef.get();
|
||||
if (originalElement == null) {
|
||||
return null;
|
||||
}
|
||||
return Pair.create(docAnchor, originalElement);
|
||||
}
|
||||
}
|
||||
@@ -24,13 +24,14 @@ import com.intellij.codeInsight.hint.HintManagerImpl;
|
||||
import com.intellij.codeInsight.hint.HintUtil;
|
||||
import com.intellij.codeInsight.navigation.actions.GotoDeclarationAction;
|
||||
import com.intellij.codeInsight.navigation.actions.GotoTypeDeclarationAction;
|
||||
import com.intellij.ide.IdeTooltip;
|
||||
import com.intellij.ide.IdeTooltipManager;
|
||||
import com.intellij.ide.util.EditSourceUtil;
|
||||
import com.intellij.lang.documentation.DocumentationProvider;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.navigation.NavigationItem;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.actionSystem.MouseShortcut;
|
||||
import com.intellij.openapi.actionSystem.Shortcut;
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton;
|
||||
import com.intellij.openapi.actionSystem.impl.PresentationFactory;
|
||||
import com.intellij.openapi.application.AccessToken;
|
||||
@@ -59,7 +60,6 @@ import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.startup.StartupManager;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -68,7 +68,8 @@ import com.intellij.pom.Navigatable;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.psi.search.searches.DefinitionsSearch;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.ui.HintListener;
|
||||
import com.intellij.ui.LightweightHint;
|
||||
import com.intellij.usageView.UsageViewShortNameLocation;
|
||||
import com.intellij.usageView.UsageViewTypeLocation;
|
||||
@@ -77,7 +78,6 @@ import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.intellij.lang.annotations.JdkConstants;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
@@ -89,14 +89,12 @@ import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EventObject;
|
||||
import java.util.List;
|
||||
|
||||
public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
|
||||
public static final DataKey<Pair<PsiElement /* documentation anchor */, PsiElement /* element under mouse */>>
|
||||
ELEMENT_UNDER_MOUSE_INFO_KEY = DataKey.create("ElementUnderMouseInfo");
|
||||
|
||||
private static final AnAction[] ourTooltipActions = {new ShowQuickDocAtPinnedWindowFromTooltipAction()};
|
||||
private static final AbstractDocumentationTooltipAction[] ourTooltipActions = {new ShowQuickDocAtPinnedWindowFromTooltipAction()};
|
||||
|
||||
private final TextAttributes ourReferenceAttributes;
|
||||
private HighlightersSet myHighlighter;
|
||||
@@ -104,8 +102,8 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
private TooltipProvider myTooltipProvider = null;
|
||||
private final FileEditorManager myFileEditorManager;
|
||||
private final DocumentationManager myDocumentationManager;
|
||||
private final IdeTooltipManager myTooltipManager;
|
||||
@Nullable private Point myPrevMouseLocation;
|
||||
private LightweightHint myHint;
|
||||
|
||||
private enum BrowseMode {None, Declaration, TypeDeclaration, Implementation}
|
||||
|
||||
@@ -170,7 +168,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
}
|
||||
MouseEvent mouseEvent = e.getMouseEvent();
|
||||
|
||||
if (isMouseOverTooltip(mouseEvent.getLocationOnScreen()) || !isMouseMovedAwayFromTooltip(mouseEvent.getLocationOnScreen())) {
|
||||
if (isMouseOverTooltip(mouseEvent.getLocationOnScreen()) || isMouseMovedTowardTooltip(mouseEvent.getLocationOnScreen())) {
|
||||
myPrevMouseLocation = mouseEvent.getLocationOnScreen();
|
||||
return;
|
||||
}
|
||||
@@ -217,7 +215,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
|
||||
public CtrlMouseHandler(final Project project, StartupManager startupManager, EditorColorsManager colorsManager,
|
||||
FileEditorManager fileEditorManager, @NotNull DocumentationManager documentationManager,
|
||||
@NotNull final EditorFactory editorFactory, @NotNull IdeTooltipManager tooltipManager)
|
||||
@NotNull final EditorFactory editorFactory)
|
||||
{
|
||||
super(project);
|
||||
startupManager.registerPostStartupActivity(new DumbAwareRunnable() {
|
||||
@@ -236,7 +234,6 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
ourReferenceAttributes = colorsManager.getGlobalScheme().getAttributes(CTRL_CLICKABLE_ATTRIBUTES_KEY);
|
||||
myFileEditorManager = fileEditorManager;
|
||||
myDocumentationManager = documentationManager;
|
||||
myTooltipManager = tooltipManager;
|
||||
myDocAlarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD, myProject);
|
||||
}
|
||||
|
||||
@@ -246,37 +243,42 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
}
|
||||
|
||||
private boolean isMouseOverTooltip(@NotNull Point mouseLocationOnScreen) {
|
||||
Rectangle bounds = getHintBounds(myTooltipManager.getCurrentTooltip());
|
||||
Rectangle bounds = getHintBounds();
|
||||
return bounds != null && bounds.contains(mouseLocationOnScreen);
|
||||
}
|
||||
|
||||
private boolean isMouseMovedAwayFromTooltip(@NotNull Point mouseLocationOnScreen) {
|
||||
Rectangle bounds = getHintBounds(myTooltipManager.getCurrentTooltip());
|
||||
private boolean isMouseMovedTowardTooltip(@NotNull Point mouseLocationOnScreen) {
|
||||
Rectangle bounds = getHintBounds();
|
||||
if (bounds == null) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Point prevLocation = myPrevMouseLocation;
|
||||
if (prevLocation == null) {
|
||||
myPrevMouseLocation = mouseLocationOnScreen;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else if (prevLocation.equals(mouseLocationOnScreen)) {
|
||||
return false;
|
||||
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 true;
|
||||
if (dx < 0 && bounds.x + bounds.width <= prevLocation.x) return true;
|
||||
if (dy > 0 && bounds.y + bounds.height >= prevLocation.y) return true;
|
||||
if (dy < 0 && bounds.y <= prevLocation.y) return true;
|
||||
|
||||
if (dx == 0 || dy == 0) {
|
||||
return false;
|
||||
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;
|
||||
@@ -286,32 +288,33 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
|
||||
// Left border.
|
||||
float crossY = a * bounds.x + b;
|
||||
if (crossY >= bounds.y && crossY < bounds.y + bounds.height) return false;
|
||||
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 false;
|
||||
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 false;
|
||||
if (crossX >= bounds.x && crossX < bounds.x + bounds.width) return true;
|
||||
|
||||
// Bottom border
|
||||
crossX = (bounds.y + bounds.height - b) / a;
|
||||
return crossX < bounds.x || crossX > bounds.x + bounds.width;
|
||||
if (crossX >= bounds.x && crossX < bounds.x + bounds.width) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Rectangle getHintBounds(@Nullable IdeTooltip tooltip) {
|
||||
if (tooltip == null) {
|
||||
private Rectangle getHintBounds() {
|
||||
LightweightHint hint = myHint;
|
||||
if (hint == null) {
|
||||
return null;
|
||||
}
|
||||
JComponent component = tooltip.getTipComponent();
|
||||
if (component == null || !component.isShowing()) {
|
||||
JComponent hintComponent = hint.getComponent();
|
||||
if (hintComponent == null || !hintComponent.isShowing()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Rectangle(component.getLocationOnScreen(), component.getBounds().getSize());
|
||||
return new Rectangle(hintComponent.getLocationOnScreen(), hintComponent.getSize());
|
||||
}
|
||||
|
||||
private static BrowseMode getBrowseMode(@JdkConstants.InputEventMask int modifiers) {
|
||||
@@ -558,7 +561,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
return found.size() != 2;
|
||||
}
|
||||
});
|
||||
return PsiUtilBase.toPsiElementArray(found);
|
||||
return PsiUtilCore.toPsiElementArray(found);
|
||||
}
|
||||
}.searchImplementations(editor, element, offset);
|
||||
if (targetElements.length > 1) {
|
||||
@@ -616,7 +619,8 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
@NotNull final DocumentationProvider provider,
|
||||
@NotNull final PsiElement originalElement,
|
||||
@NotNull final PsiElement anchorElement,
|
||||
@NotNull final Consumer<String> newTextConsumer)
|
||||
@NotNull final Consumer<String> newTextConsumer,
|
||||
@NotNull final LightweightHint hint)
|
||||
{
|
||||
myDocAlarm.cancelAllRequests();
|
||||
myDocAlarm.addRequest(new Runnable() {
|
||||
@@ -637,44 +641,41 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
if (fullText == null) {
|
||||
return;
|
||||
}
|
||||
final String updatedText = DocPreviewUtil.buildPreview(header, qualifiedNameRef.get(), fullText);
|
||||
String updatedText = DocPreviewUtil.buildPreview(header, qualifiedNameRef.get(), fullText);
|
||||
final String newHtml = HintUtil.prepareHintText(updatedText, HintUtil.getInformationHint());
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// There is a possible case that quick doc control bounds are changed, e.g. it contained text
|
||||
// like 'public final class String implements java.io.Serializable, Comparable<String>, CharSequence' and
|
||||
// There is a possible case that quick doc control width is changed, e.g. it contained text
|
||||
// like 'public final class String implements java.io.Serializable, java.lang.Comparable<java.lang.String>' and
|
||||
// new text replaces fully-qualified class names by hyperlinks with short name.
|
||||
// That's why we might need to update the control size. We assume that the hint component is located at the
|
||||
// layered pane, so, the algorithm is to find an ancestor layered pane and apply new size for the target component.
|
||||
|
||||
JComponent tipComponent = null;
|
||||
Dimension oldSize = null;
|
||||
IdeTooltip tooltip = myTooltipManager.getCurrentTooltip();
|
||||
if (tooltip != null) {
|
||||
tipComponent = tooltip.getTipComponent();
|
||||
if (tipComponent != null) {
|
||||
oldSize = tipComponent.getPreferredSize();
|
||||
}
|
||||
Dimension oldSize = hint.getComponent().getPreferredSize();
|
||||
newTextConsumer.consume(newHtml);
|
||||
|
||||
if (oldSize == null) {
|
||||
return;
|
||||
}
|
||||
newTextConsumer.consume(updatedText);
|
||||
int widthChange = 0;
|
||||
int heightChange = 0;
|
||||
if (oldSize != null) {
|
||||
Dimension newSize = tipComponent.getPreferredSize();
|
||||
if (!oldSize.equals(newSize)) {
|
||||
widthChange = newSize.width - oldSize.width;
|
||||
heightChange = newSize.height - oldSize.height;
|
||||
}
|
||||
|
||||
Dimension newSize = hint.getComponent().getPreferredSize();
|
||||
if (newSize.width == oldSize.width) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (widthChange == 0 && heightChange == 0) {
|
||||
|
||||
// We're assuming here that there are two possible hint representation modes: popup and layered pane. So, we update
|
||||
// component size and pack the popup in the first case and set new content bounds at the second one.
|
||||
if (hint.isRealPopup()) {
|
||||
hint.getComponent().setPreferredSize(new Dimension(newSize.width, oldSize.height));
|
||||
hint.pack();
|
||||
return;
|
||||
}
|
||||
|
||||
Container topLevelLayeredPaneChild = null;
|
||||
boolean adjustBounds = false;
|
||||
for (Container current = tipComponent.getParent(); current != null; current = current.getParent()) {
|
||||
for (Container current = hint.getComponent().getParent(); current != null; current = current.getParent()) {
|
||||
if (current instanceof JLayeredPane) {
|
||||
adjustBounds = true;
|
||||
break;
|
||||
@@ -686,7 +687,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
|
||||
if (adjustBounds && topLevelLayeredPaneChild != null) {
|
||||
Rectangle bounds = topLevelLayeredPaneChild.getBounds();
|
||||
topLevelLayeredPaneChild.setBounds(bounds.x, bounds.y, bounds.width + widthChange, bounds.height + heightChange);
|
||||
topLevelLayeredPaneChild.setBounds(bounds.x, bounds.y, bounds.width + newSize.width - oldSize.width, bounds.height);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -731,7 +732,6 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
int selEnd = myEditor.getSelectionModel().getSelectionEnd();
|
||||
|
||||
if (offset >= selStart && offset < selEnd) return;
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
|
||||
public void run() {
|
||||
ProgressIndicatorUtils.runWithWriteActionPriority(new Runnable() {
|
||||
@@ -814,10 +814,6 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
Ref<Consumer<String>> newTextConsumerRef = new Ref<Consumer<String>>();
|
||||
JComponent label = HintUtil.createInformationLabel(docInfo.text, hyperlinkListener, mouseListener, newTextConsumerRef);
|
||||
Consumer<String> newTextConsumer = newTextConsumerRef.get();
|
||||
myDocAlarm.cancelAllRequests();
|
||||
if (newTextConsumer != null && docInfo.docProvider != null && docInfo.documentationAnchor != null) {
|
||||
fulfillDocInfo(docInfo.text, docInfo.docProvider, info.myElementAtPointer, docInfo.documentationAnchor, newTextConsumer);
|
||||
}
|
||||
QuickDocInfoPane quickDocPane = null;
|
||||
if (docInfo.documentationAnchor != null) {
|
||||
quickDocPane = new QuickDocInfoPane(docInfo.documentationAnchor, info.myElementAtPointer, label);
|
||||
@@ -825,7 +821,20 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
}
|
||||
|
||||
JComponent hintContent = quickDocPane == null ? label : quickDocPane;
|
||||
|
||||
final LightweightHint hint = new LightweightHint(hintContent);
|
||||
myHint = hint;
|
||||
hint.addHintListener(new HintListener() {
|
||||
@Override
|
||||
public void hintHidden(EventObject event) {
|
||||
myHint = null;
|
||||
}
|
||||
});
|
||||
myDocAlarm.cancelAllRequests();
|
||||
if (newTextConsumer != null && docInfo.docProvider != null && docInfo.documentationAnchor != null) {
|
||||
fulfillDocInfo(docInfo.text, docInfo.docProvider, info.myElementAtPointer, docInfo.documentationAnchor, newTextConsumer, hint);
|
||||
}
|
||||
|
||||
final HintManagerImpl hintManager = HintManagerImpl.getInstanceImpl();
|
||||
Point p = HintManagerImpl.getHintPosition(hint, myEditor, myPosition, HintManager.ABOVE);
|
||||
hintManager.showEditorHint(hint, myEditor, p,
|
||||
@@ -899,26 +908,26 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private class QuickDocInfoPane extends JLayeredPane implements DataProvider {
|
||||
private class QuickDocInfoPane extends JLayeredPane {
|
||||
|
||||
private static final int BUTTON_HGAP = 5;
|
||||
|
||||
@NotNull private final List<JComponent> myButtons = new ArrayList<JComponent>();
|
||||
@NotNull private final Pair<PsiElement, PsiElement> myElementUnderMouseInfo;
|
||||
|
||||
@NotNull private final JComponent myBaseDocControl;
|
||||
|
||||
private final int myMinWidth;
|
||||
private final int myMinHeight;
|
||||
|
||||
QuickDocInfoPane(@NotNull PsiElement documentationAnchor, @NotNull PsiElement elementUnderMouse, @NotNull JComponent baseDocControl) {
|
||||
myElementUnderMouseInfo = Pair.create(documentationAnchor, elementUnderMouse);
|
||||
myBaseDocControl = baseDocControl;
|
||||
|
||||
PresentationFactory presentationFactory = new PresentationFactory();
|
||||
for (AnAction action : ourTooltipActions) {
|
||||
for (AbstractDocumentationTooltipAction action : ourTooltipActions) {
|
||||
Icon icon = action.getTemplatePresentation().getIcon();
|
||||
Dimension minSize = new Dimension(icon.getIconWidth(), icon.getIconHeight());
|
||||
myButtons.add(new ActionButton(action, presentationFactory.getPresentation(action), IdeTooltipManager.IDE_TOOLTIP_PLACE, minSize));
|
||||
action.setDocInfo(documentationAnchor, elementUnderMouse);
|
||||
}
|
||||
Collections.reverse(myButtons);
|
||||
|
||||
@@ -945,13 +954,14 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getData(@NonNls String dataId) {
|
||||
return ELEMENT_UNDER_MOUSE_INFO_KEY.is(dataId) ? myElementUnderMouseInfo : null;
|
||||
public Dimension getPreferredSize() {
|
||||
return expandIfNecessary(myBaseDocControl.getPreferredSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return expandIfNecessary(myBaseDocControl.getPreferredSize());
|
||||
public void setPreferredSize(Dimension preferredSize) {
|
||||
super.setPreferredSize(preferredSize);
|
||||
myBaseDocControl.setPreferredSize(preferredSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -975,7 +985,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
@Override
|
||||
public void doLayout() {
|
||||
Rectangle bounds = getBounds();
|
||||
myBaseDocControl.setBounds(bounds);
|
||||
myBaseDocControl.setBounds(new Rectangle(0, 0, bounds.width, bounds.height));
|
||||
|
||||
int x = bounds.width;
|
||||
for (JComponent button : myButtons) {
|
||||
@@ -1035,7 +1045,10 @@ public class CtrlMouseHandler extends AbstractProjectComponent {
|
||||
|
||||
final PsiElement targetElement = myProvider.getDocumentationElementForLink(PsiManager.getInstance(myProject), elementName, myContext);
|
||||
if (targetElement != null) {
|
||||
myTooltipManager.hideCurrentNow(false);
|
||||
LightweightHint hint = myHint;
|
||||
if (hint != null) {
|
||||
hint.hide(true);
|
||||
}
|
||||
myDocumentationManager.showJavaDocInfo(targetElement, myContext, true, null);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-51
@@ -17,32 +17,18 @@ package com.intellij.codeInsight.navigation;
|
||||
|
||||
import com.intellij.codeInsight.documentation.DocumentationManager;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.ide.IdeTooltip;
|
||||
import com.intellij.ide.IdeTooltipManager;
|
||||
import com.intellij.idea.ActionsBundle;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/13/12 11:43 AM
|
||||
*/
|
||||
public class ShowQuickDocAtPinnedWindowFromTooltipAction extends AnAction {
|
||||
|
||||
@NotNull private final IdeTooltipManager myTooltipManager = IdeTooltipManager.getInstance();
|
||||
@NotNull private final DataManager myDataManager = DataManager.getInstance();
|
||||
|
||||
@Nullable private WeakReference<Pair<PsiElement, PsiElement>> myInfo;
|
||||
public class ShowQuickDocAtPinnedWindowFromTooltipAction extends AbstractDocumentationTooltipAction {
|
||||
|
||||
public ShowQuickDocAtPinnedWindowFromTooltipAction() {
|
||||
String className = getClass().getSimpleName();
|
||||
@@ -53,45 +39,14 @@ public class ShowQuickDocAtPinnedWindowFromTooltipAction extends AnAction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(AnActionEvent e) {
|
||||
|
||||
// We can't use data context from the given event because it's built from the focused component and IDE tooltip doesn't have focus.
|
||||
IdeTooltip tooltip = myTooltipManager.getCurrentTooltip();
|
||||
if (tooltip == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
JComponent component = tooltip.getTipComponent();
|
||||
if (component == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<PsiElement, PsiElement> info = CtrlMouseHandler.ELEMENT_UNDER_MOUSE_INFO_KEY.getData(myDataManager.getDataContext(component));
|
||||
if (info != null) {
|
||||
// Target info is retrieved during AnAction.update() processing because IDE tooltip is closed on action activation,
|
||||
// i.e. IdeTooltipManager.getCurrentComponent() returns null during AnAction.actionPerformed() execution.
|
||||
myInfo = new WeakReference<Pair<PsiElement, PsiElement>>(info);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
WeakReference<Pair<PsiElement, PsiElement>> infoRef = myInfo;
|
||||
if (infoRef == null) {
|
||||
return;
|
||||
}
|
||||
Pair<PsiElement, PsiElement> info = infoRef.get();
|
||||
if (info == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext());
|
||||
protected void doActionPerformed(@NotNull DataContext context, @NotNull PsiElement docAnchor, @NotNull PsiElement originalElement) {
|
||||
Project project = PlatformDataKeys.PROJECT.getData(context);
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
myInfo = null;
|
||||
DocumentationManager docManager = DocumentationManager.getInstance(project);
|
||||
docManager.showJavaDocInfoAtToolWindow(info.first, info.second);
|
||||
docManager.setAllowContentUpdateFromContext(false);
|
||||
docManager.showJavaDocInfoAtToolWindow(docAnchor, originalElement);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -21,6 +21,7 @@ import com.intellij.codeHighlighting.HighlightDisplayLevel;
|
||||
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
|
||||
import com.intellij.codeInsight.daemon.impl.SeverityRegistrar;
|
||||
import com.intellij.codeInsight.hint.HintUtil;
|
||||
import com.intellij.codeInspection.InspectionProfile;
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
@@ -32,7 +33,6 @@ import com.intellij.codeInspection.ex.ScopeToolState;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.CommonActionsManager;
|
||||
import com.intellij.ide.DefaultTreeExpander;
|
||||
import com.intellij.ide.IdeTooltipManager;
|
||||
import com.intellij.ide.TreeExpander;
|
||||
import com.intellij.ide.ui.search.SearchUtil;
|
||||
import com.intellij.ide.ui.search.SearchableOptionsRegistrar;
|
||||
@@ -729,7 +729,7 @@ public class SingleInspectionProfilePanel extends JPanel {
|
||||
try {
|
||||
final HintHint hintHint = new HintHint(myBrowser, new Point(0, 0));
|
||||
hintHint.setFont(myBrowser.getFont());
|
||||
myBrowser.read(new StringReader(SearchUtil.markup(IdeTooltipManager.formatHtml(description, hintHint), myProfileFilter.getFilter())), null);
|
||||
myBrowser.read(new StringReader(SearchUtil.markup(HintUtil.prepareHintText(description, hintHint), myProfileFilter.getFilter())), null);
|
||||
}
|
||||
catch (IOException e2) {
|
||||
try {
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.ui.HintHint;
|
||||
import com.intellij.ui.SimpleColoredComponent;
|
||||
import com.intellij.ui.SimpleColoredText;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ui.Html;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.intellij.lang.annotations.JdkConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -58,7 +59,7 @@ public class HintUtil {
|
||||
@Nullable MouseListener mouseListener,
|
||||
@Nullable Ref<Consumer<String>> updatedTextConsumer)
|
||||
{
|
||||
HintHint hintHint = new HintHint().setTextBg(INFORMATION_COLOR).setTextFg(Color.black).setFont(getBoldFont()).setAwtTooltip(true);
|
||||
HintHint hintHint = getInformationHint();
|
||||
|
||||
final HintLabel label = new HintLabel();
|
||||
label.setText(text, hintHint);
|
||||
@@ -86,11 +87,8 @@ public class HintUtil {
|
||||
label.myPane.setText(s);
|
||||
|
||||
// Force preferred size recalculation.
|
||||
label.setPreferredSize(null);
|
||||
label.myPane.setPreferredSize(null);
|
||||
if (label.myPane.getPreferredSize() == null && size != null) {
|
||||
// Fallback to the old preferred size if it's not recalculated.
|
||||
label.myPane.setPreferredSize(size);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -98,6 +96,11 @@ public class HintUtil {
|
||||
return label;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HintHint getInformationHint() {
|
||||
return new HintHint().setTextBg(INFORMATION_COLOR).setTextFg(Color.black).setFont(getBoldFont()).setAwtTooltip(true);
|
||||
}
|
||||
|
||||
public static CompoundBorder createHintBorder() {
|
||||
return BorderFactory.createCompoundBorder(
|
||||
new ColoredSideBorder(Color.white, Color.white, Color.gray, Color.gray, 1),
|
||||
@@ -173,6 +176,20 @@ public class HintUtil {
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String prepareHintText(@NotNull String text, @NotNull HintHint hintHint) {
|
||||
return prepareHintText(new Html(text), hintHint);
|
||||
}
|
||||
|
||||
public static String prepareHintText(@NotNull Html text, @NotNull HintHint hintHint) {
|
||||
String htmlBody = UIUtil.getHtmlBody(text);
|
||||
return String.format(
|
||||
"<html><head>%s</head><body>%s</body></html>",
|
||||
UIUtil.getCssFontDeclaration(hintHint.getTextFont(), hintHint.getTextForeground(), hintHint.getLinkForeground(), hintHint.getUlImg()),
|
||||
htmlBody
|
||||
);
|
||||
}
|
||||
|
||||
private static class HintLabel extends JPanel {
|
||||
|
||||
|
||||
@@ -513,13 +513,7 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener
|
||||
|
||||
public static JEditorPane initPane(@NonNls Html html, final HintHint hintHint, @Nullable final JLayeredPane layeredPane) {
|
||||
final Ref<Dimension> prefSize = new Ref<Dimension>(null);
|
||||
String htmlBody = UIUtil.getHtmlBody(html);
|
||||
@NonNls String text = "<html><head>" +
|
||||
UIUtil.getCssFontDeclaration(hintHint.getTextFont(), hintHint.getTextForeground(), hintHint.getLinkForeground(),
|
||||
hintHint.getUlImg()) +
|
||||
"</head><body>" +
|
||||
htmlBody +
|
||||
"</body></html>";
|
||||
@NonNls String text = HintUtil.prepareHintText(html, hintHint);
|
||||
|
||||
final boolean[] prefSizeWasComputed = {false};
|
||||
final JEditorPane pane = new JEditorPane() {
|
||||
@@ -626,17 +620,6 @@ public class IdeTooltipManager implements ApplicationComponent, AWTEventListener
|
||||
return pane;
|
||||
}
|
||||
|
||||
public static String formatHtml(@NonNls String text, HintHint hintHint) {
|
||||
String htmlBody = UIUtil.getHtmlBody(text);
|
||||
text = "<html><head>" +
|
||||
UIUtil.getCssFontDeclaration(hintHint.getTextFont(), hintHint.getTextForeground(), hintHint.getLinkForeground(),
|
||||
hintHint.getUlImg()) +
|
||||
"</head><body>" +
|
||||
htmlBody +
|
||||
"</body></html>";
|
||||
return text;
|
||||
}
|
||||
|
||||
public static void setColors(JComponent pane) {
|
||||
pane.setForeground(Color.black);
|
||||
pane.setBackground(HintUtil.INFORMATION_COLOR);
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.intellij.ui.components.panels.OpaquePanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.LineBorder;
|
||||
import javax.swing.event.EventListenerList;
|
||||
import java.awt.*;
|
||||
@@ -458,21 +459,44 @@ public class LightweightHint extends UserDataHolderBase implements Hint {
|
||||
}
|
||||
}
|
||||
|
||||
public void setSize(Dimension size) {
|
||||
if (myIsRealPopup) {
|
||||
myPopup.setSize(size);
|
||||
public void setSize(final Dimension size) {
|
||||
if (myIsRealPopup && myPopup != null) {
|
||||
// There is a possible case that a popup wraps target content component into other components which might have borders.
|
||||
// That's why we can't just apply component's size to the whole popup. It needs to be adjusted before that.
|
||||
JComponent popupContent = myPopup.getContent();
|
||||
int widthExpand = 0;
|
||||
int heightExpand = 0;
|
||||
boolean adjustSize = false;
|
||||
JComponent prev = myComponent;
|
||||
for (Container c = myComponent.getParent(); c != null; c = c.getParent()) {
|
||||
if (c == popupContent) {
|
||||
adjustSize = true;
|
||||
break;
|
||||
}
|
||||
if (c instanceof JComponent) {
|
||||
Border border = ((JComponent)c).getBorder();
|
||||
if (prev != null && border != null) {
|
||||
Insets insets = border.getBorderInsets(prev);
|
||||
widthExpand += insets.left + insets.right;
|
||||
heightExpand += insets.top + insets.bottom;
|
||||
}
|
||||
prev = (JComponent)c;
|
||||
}
|
||||
else {
|
||||
prev = null;
|
||||
}
|
||||
}
|
||||
Dimension sizeToUse = size;
|
||||
if (adjustSize && (widthExpand != 0 || heightExpand != 0)) {
|
||||
sizeToUse = new Dimension(size.width + widthExpand, size.height + heightExpand);
|
||||
}
|
||||
myPopup.setSize(sizeToUse);
|
||||
}
|
||||
else {
|
||||
//todo kirillk
|
||||
if (isAwtTooltip()) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
myComponent.setSize(size);
|
||||
else if (!isAwtTooltip()) {
|
||||
myComponent.setSize(size);
|
||||
|
||||
myComponent.revalidate();
|
||||
myComponent.repaint();
|
||||
}
|
||||
myComponent.revalidate();
|
||||
myComponent.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user