IDEA-237977 Rendered JavaDoc not selectable

GitOrigin-RevId: e9250377047c1fe84d5bd8c3e5c72f713fec0f10
This commit is contained in:
Dmitry Batrak
2020-05-29 11:47:07 +03:00
committed by intellij-monorepo-bot
parent ae0f480588
commit 71cd340039
5 changed files with 91 additions and 46 deletions
@@ -30,9 +30,8 @@ import java.util.EventObject;
* dispatching, and is not guaranteed to be actual by the time some event listener receives the event (if previously called listener has
* modified the editor state).
* <p>
* The additional information is currently provided for the following types of events: {@link MouseEvent#MOUSE_MOVED MOUSE_MOVED},
* {@link MouseEvent#MOUSE_PRESSED MOUSE_PRESSED}, {@link MouseEvent#MOUSE_RELEASED MOUSE_RELEASED} and
* {@link MouseEvent#MOUSE_CLICKED MOUSE_CLICKED}. For other events, return values of corresponding event getters are unspecified.
* The additional information is not provided for {@link MouseEvent#MOUSE_ENTERED MOUSE_ENTERED} and
* {@link MouseEvent#MOUSE_EXITED MOUSE_EXITED} events. Return values of corresponding event getters are unspecified for those events.
*/
public class EditorMouseEvent extends EventObject {
@NotNull
@@ -88,52 +87,33 @@ public class EditorMouseEvent extends EventObject {
return myEditorArea;
}
/**
* See {@link EditorMouseEvent class documentation} with regard to the availability of this information.
*/
public int getOffset() {
return myOffset;
}
/**
* See {@link EditorMouseEvent class documentation} with regard to the availability of this information.
*/
public @NotNull LogicalPosition getLogicalPosition() {
return myLogicalPosition;
}
/**
* See {@link EditorMouseEvent class documentation} with regard to the availability of this information.
*/
public @NotNull VisualPosition getVisualPosition() {
return myVisualPosition;
}
/**
* Returns {@code false} if mouse is below the last line of text, to the right of the last character on the line, or over an inlay.
* See {@link EditorMouseEvent class documentation} with regard to the availability of this information.
*/
public boolean isOverText() {
return myIsOverText;
}
/**
* See {@link EditorMouseEvent class documentation} with regard to the availability of this information.
*/
public @Nullable FoldRegion getCollapsedFoldRegion() {
return myCollapsedFoldRegion == null || !myCollapsedFoldRegion.isValid() ? null : myCollapsedFoldRegion;
}
/**
* See {@link EditorMouseEvent class documentation} with regard to the availability of this information.
*/
public @Nullable Inlay getInlay() {
return myInlay == null || !myInlay.isValid() ? null : myInlay;
}
/**
* See {@link EditorMouseEvent class documentation} with regard to the availability of this information.
*/
public @Nullable GutterIconRenderer getGutterIconRenderer() {
return myGutterIconRenderer;
}
@@ -196,6 +196,7 @@ doc.render.dumb.mode.text=<i>Cannot render documentation during indexing</i>
doc.render.loading.text=Loading...
doc.render.not.available.text=<i>Not available</i>
doc.render.gutter.icon.setting=Documentation comments in-place rendering
doc.render.copy.action.text=Copy Selection
declaration.navigation.title=Choose Declaration
declaration.navigation.nowhere.to.go=Cannot find declaration to go to
template.shortcut.enter=Enter
@@ -13,30 +13,39 @@ import org.jetbrains.annotations.Nullable;
import sun.awt.AWTAccessor;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
class DocRenderMouseEventBridge implements EditorMouseListener, EditorMouseMotionListener {
private DocRenderer.EditorPane myCurrentPane;
private DocRenderer.EditorPane myMouseOverPane;
private DocRenderer.EditorPane myDragPane;
@Override
public void mouseMoved(@NotNull EditorMouseEvent event) {
if (event.getArea() != EditorMouseEventArea.EDITING_AREA) return;
DocRenderer.EditorPane currentPane = redispatchEvent(event, MouseEvent.MOUSE_MOVED);
DocRenderer.EditorPane currentPane = redispatchEvent(event, MouseEvent.MOUSE_MOVED, null);
if (currentPane == null) {
restoreCursor();
}
else {
((EditorEx)event.getEditor()).setCustomCursor(DocRenderMouseEventBridge.class, currentPane.getCursor());
if (currentPane != myCurrentPane) {
if (myCurrentPane != null) {
dispatchMouseExitEvent(myCurrentPane);
if (currentPane != myMouseOverPane) {
if (myMouseOverPane != null) {
dispatchMouseExitEvent(myMouseOverPane);
}
myCurrentPane = currentPane;
myMouseOverPane = currentPane;
}
}
}
@Override
public void mouseDragged(@NotNull EditorMouseEvent e) {
if (e.getArea() != EditorMouseEventArea.EDITING_AREA) return;
redispatchEvent(e, MouseEvent.MOUSE_DRAGGED, myDragPane);
}
@Override
public void mouseExited(@NotNull EditorMouseEvent event) {
if (event.getArea() != EditorMouseEventArea.EDITING_AREA) return;
@@ -44,23 +53,42 @@ class DocRenderMouseEventBridge implements EditorMouseListener, EditorMouseMotio
restoreCursor();
}
@Override
public void mousePressed(@NotNull EditorMouseEvent event) {
if (event.getArea() != EditorMouseEventArea.EDITING_AREA) return;
myDragPane = redispatchEvent(event, MouseEvent.MOUSE_PRESSED, null);
}
@Override
public void mouseReleased(@NotNull EditorMouseEvent event) {
if (event.getArea() != EditorMouseEventArea.EDITING_AREA) return;
redispatchEvent(event, MouseEvent.MOUSE_RELEASED, null);
myDragPane = null;
}
@Override
public void mouseClicked(@NotNull EditorMouseEvent event) {
if (event.getArea() != EditorMouseEventArea.EDITING_AREA) return;
redispatchEvent(event, MouseEvent.MOUSE_CLICKED);
redispatchEvent(event, MouseEvent.MOUSE_CLICKED, null);
}
private void restoreCursor() {
if (myCurrentPane != null) {
dispatchMouseExitEvent(myCurrentPane);
((EditorEx)myCurrentPane.getEditor()).setCustomCursor(DocRenderMouseEventBridge.class, null);
myCurrentPane = null;
if (myMouseOverPane != null) {
dispatchMouseExitEvent(myMouseOverPane);
((EditorEx)myMouseOverPane.getEditor()).setCustomCursor(DocRenderMouseEventBridge.class, null);
myMouseOverPane = null;
}
}
@Nullable
private static DocRenderer.EditorPane redispatchEvent(@NotNull EditorMouseEvent event, int eventId) {
private static DocRenderer.EditorPane redispatchEvent(@NotNull EditorMouseEvent event,
// we need a separately passed eventId because EditorImpl dispatches MOUSE_RELEASED
// events to both 'mouseReleased' and 'mouseClicked' methods in listeners
int eventId,
@Nullable DocRenderer.EditorPane targetPane) {
MouseEvent mouseEvent = event.getMouseEvent();
Point mousePoint = mouseEvent.getPoint();
Inlay inlay = event.getInlay();
@@ -74,11 +102,14 @@ class DocRenderMouseEventBridge implements EditorMouseListener, EditorMouseMotio
int y = mousePoint.y - inlayBounds.y - relativeBounds.y;
if (x >= 0 && x < relativeBounds.width && y >= 0 && y < relativeBounds.height) {
DocRenderer.EditorPane editorPane = ((DocRenderer)renderer).getRendererComponent(inlay, relativeBounds.width);
int button = mouseEvent.getButton();
dispatchEvent(editorPane, new MouseEvent(editorPane, eventId, 0, 0, x, y, mouseEvent.getClickCount(), false,
// hack to process middle-button clicks (JEditorPane ignores them)
button == MouseEvent.BUTTON2 ? MouseEvent.BUTTON1 : button));
return editorPane;
if (targetPane == null || targetPane == editorPane) {
int button = mouseEvent.getButton();
int modifiers = eventId == MouseEvent.MOUSE_DRAGGED && button == MouseEvent.BUTTON1 ? InputEvent.BUTTON1_DOWN_MASK : 0;
dispatchEvent(editorPane, new MouseEvent(editorPane, eventId, 0, modifiers, x, y, mouseEvent.getClickCount(), false,
// hack to process middle-button clicks (JEditorPane ignores them)
button == MouseEvent.BUTTON2 ? MouseEvent.BUTTON1 : button));
return editorPane;
}
}
}
}
@@ -9,10 +9,7 @@ import com.intellij.icons.AllIcons;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.ui.UISettings;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.actionSystem.MouseShortcut;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
@@ -22,8 +19,10 @@ import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
@@ -42,6 +41,7 @@ import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.StartupUiUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
@@ -50,6 +50,7 @@ import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.ImageView;
import javax.swing.text.html.StyleSheet;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.MouseEvent;
import java.awt.font.TextAttribute;
import java.awt.image.ImageObserver;
@@ -159,7 +160,12 @@ class DocRenderer implements EditorCustomElementRenderer {
@Override
public ActionGroup getContextMenuGroup(@NotNull Inlay inlay) {
return new DefaultActionGroup(myItem.createToggleAction(), new DocRenderItem.ChangeFontSize());
DefaultActionGroup group = new DefaultActionGroup();
group.add(new CopySelection());
group.addSeparator();
group.add(myItem.createToggleAction());
group.add(new DocRenderItem.ChangeFontSize());
return group;
}
private static int scale(int value) {
@@ -200,6 +206,7 @@ class DocRenderer implements EditorCustomElementRenderer {
clearCachedComponent();
myPane = new EditorPane();
myPane.setEditable(false);
myPane.getCaret().setSelectionVisible(true);
myPane.putClientProperty("caretWidth", 0); // do not reserve space for caret (making content one pixel narrower than component)
myPane.setEditorKit(createEditorKit(editor));
myPane.setBorder(JBUI.Borders.empty());
@@ -208,7 +215,10 @@ class DocRenderer implements EditorCustomElementRenderer {
// disable kerning for now - laying out all fragments in a file with it takes too much time
fontAttributes.put(TextAttribute.KERNING, 0);
myPane.setFont(myPane.getFont().deriveFont(fontAttributes));
myPane.setForeground(getTextColor(editor.getColorsScheme()));
Color textColor = getTextColor(editor.getColorsScheme());
myPane.setForeground(textColor);
myPane.setSelectedTextColor(textColor);
myPane.setSelectionColor(editor.getSelectionModel().getTextAttributes().getBackgroundColor());
UIUtil.enableEagerSoftWrapping(myPane);
String textToRender = myItem.textToRender;
if (textToRender == null) {
@@ -330,6 +340,10 @@ class DocRenderer implements EditorCustomElementRenderer {
}
}
@Nullable String getSelectedText() {
return myPane == null ? null : myPane.getSelectedText();
}
private static EditorKit createEditorKit(@NotNull Editor editor) {
HTMLEditorKit editorKit = new MyEditorKit();
editorKit.getStyleSheet().addStyleSheet(getStyleSheet(editor));
@@ -568,4 +582,23 @@ class DocRenderer implements EditorCustomElementRenderer {
super.paint(scalingGraphics, a);
}
}
private class CopySelection extends DumbAwareAction {
CopySelection() {
super(CodeInsightBundle.messagePointer("doc.render.copy.action.text"), AllIcons.Actions.Copy);
}
@Override
public void update(@NotNull AnActionEvent e) {
e.getPresentation().setVisible(!StringUtil.isEmpty(getSelectedText()));
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
String text = getSelectedText();
if (!StringUtil.isEmpty(text)) {
CopyPasteManager.getInstance().setContents(new StringSelection(text));
}
}
}
}
@@ -4284,7 +4284,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
validateMousePointer(e, null);
((TransactionGuardImpl)TransactionGuard.getInstance()).performUserActivity(() -> runMouseDraggedCommand(e));
EditorMouseEvent event = new EditorMouseEvent(EditorImpl.this, e, getMouseEventArea(e));
EditorMouseEvent event = createEditorMouseEvent(e);
if (event.getArea() == EditorMouseEventArea.LINE_MARKERS_AREA) {
myGutterComponent.mouseDragged(e);
}