mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-04 17:20:55 +07:00
IDEA-270099 Spring: compute event targets in gutter lazily
Support asynchronous mode for NavigationGutterIconRenderer popup calculation GitOrigin-RevId: 69dc72c71b9c3e6b80bc64ec70fc219c2e409d1f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
557da2e0bb
commit
a79bb806ad
@@ -53,7 +53,7 @@ public class NavigationGutterIconBuilder<T> {
|
|||||||
private final NotNullFunction<? super T, ? extends Collection<? extends PsiElement>> myConverter;
|
private final NotNullFunction<? super T, ? extends Collection<? extends PsiElement>> myConverter;
|
||||||
|
|
||||||
protected NotNullLazyValue<Collection<? extends T>> myTargets;
|
protected NotNullLazyValue<Collection<? extends T>> myTargets;
|
||||||
private boolean myLazy;
|
protected boolean myLazy;
|
||||||
protected @Tooltip String myTooltipText;
|
protected @Tooltip String myTooltipText;
|
||||||
protected @PopupTitle String myPopupTitle;
|
protected @PopupTitle String myPopupTitle;
|
||||||
protected @PopupContent String myEmptyText;
|
protected @PopupContent String myEmptyText;
|
||||||
@@ -288,11 +288,18 @@ public class NavigationGutterIconBuilder<T> {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
protected NavigationGutterIconRenderer createGutterIconRenderer(@NotNull NotNullLazyValue<List<SmartPsiElementPointer<?>>> pointers,
|
protected NavigationGutterIconRenderer createGutterIconRenderer(@NotNull NotNullLazyValue<List<SmartPsiElementPointer<?>>> pointers,
|
||||||
@NotNull Computable<PsiElementListCellRenderer<?>> renderer,
|
@NotNull Computable<PsiElementListCellRenderer<?>> renderer,
|
||||||
boolean empty) {
|
boolean empty) {
|
||||||
return new MyNavigationGutterIconRenderer(this, myAlignment, myIcon, myTooltipText, pointers, renderer, empty);
|
return new MyNavigationGutterIconRenderer(this, myAlignment, myIcon, myTooltipText, pointers, renderer, empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected NavigationGutterIconRenderer createLazyGutterIconRenderer(@NotNull NotNullLazyValue<List<SmartPsiElementPointer<?>>> pointers,
|
||||||
|
@NotNull Computable<PsiElementListCellRenderer<?>> renderer,
|
||||||
|
boolean empty) {
|
||||||
|
return new MyNavigationGutterIconRenderer(this, myAlignment, myIcon, myTooltipText, pointers, renderer, empty, true);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static <T> NotNullLazyValue<List<SmartPsiElementPointer<?>>> createPointersThunk(boolean lazy,
|
private static <T> NotNullLazyValue<List<SmartPsiElementPointer<?>>> createPointersThunk(boolean lazy,
|
||||||
final Project project,
|
final Project project,
|
||||||
@@ -363,6 +370,21 @@ public class NavigationGutterIconBuilder<T> {
|
|||||||
myEmpty = empty;
|
myEmpty = empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MyNavigationGutterIconRenderer(@NotNull NavigationGutterIconBuilder<?> builder,
|
||||||
|
@NotNull Alignment alignment,
|
||||||
|
Icon icon,
|
||||||
|
@Nullable @Tooltip String tooltipText,
|
||||||
|
@NotNull NotNullLazyValue<List<SmartPsiElementPointer<?>>> pointers,
|
||||||
|
@NotNull Computable<PsiElementListCellRenderer<?>> cellRenderer,
|
||||||
|
boolean empty,
|
||||||
|
boolean computeTargetsInBackground) {
|
||||||
|
super(builder.myPopupTitle, builder.myEmptyText, cellRenderer, pointers, computeTargetsInBackground);
|
||||||
|
myAlignment = alignment;
|
||||||
|
myIcon = icon;
|
||||||
|
myTooltipText = tooltipText;
|
||||||
|
myEmpty = empty;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNavigateAction() {
|
public boolean isNavigateAction() {
|
||||||
return !myEmpty;
|
return !myEmpty;
|
||||||
|
|||||||
@@ -20,8 +20,11 @@ import com.intellij.codeInsight.hint.HintUtil;
|
|||||||
import com.intellij.ide.util.PsiElementListCellRenderer;
|
import com.intellij.ide.util.PsiElementListCellRenderer;
|
||||||
import com.intellij.openapi.actionSystem.AnAction;
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.editor.markup.GutterIconRenderer;
|
import com.intellij.openapi.editor.markup.GutterIconRenderer;
|
||||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||||
|
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||||
|
import com.intellij.openapi.progress.ProgressManager;
|
||||||
import com.intellij.openapi.project.DumbAware;
|
import com.intellij.openapi.project.DumbAware;
|
||||||
import com.intellij.openapi.project.DumbService;
|
import com.intellij.openapi.project.DumbService;
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
@@ -32,23 +35,32 @@ import com.intellij.openapi.util.Computable;
|
|||||||
import com.intellij.openapi.util.NlsContexts.PopupContent;
|
import com.intellij.openapi.util.NlsContexts.PopupContent;
|
||||||
import com.intellij.openapi.util.NlsContexts.PopupTitle;
|
import com.intellij.openapi.util.NlsContexts.PopupTitle;
|
||||||
import com.intellij.openapi.util.NotNullLazyValue;
|
import com.intellij.openapi.util.NotNullLazyValue;
|
||||||
|
import com.intellij.openapi.util.Ref;
|
||||||
import com.intellij.openapi.util.Segment;
|
import com.intellij.openapi.util.Segment;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import com.intellij.pom.Navigatable;
|
import com.intellij.pom.Navigatable;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.SmartPsiElementPointer;
|
import com.intellij.psi.SmartPsiElementPointer;
|
||||||
import com.intellij.psi.util.PsiUtilCore;
|
import com.intellij.psi.util.PsiUtilCore;
|
||||||
|
import com.intellij.ui.AnimatedIcon;
|
||||||
import com.intellij.ui.awt.RelativePoint;
|
import com.intellij.ui.awt.RelativePoint;
|
||||||
|
import com.intellij.util.concurrency.AppExecutorUtil;
|
||||||
|
import com.intellij.util.concurrency.EdtScheduledExecutorService;
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
import com.intellij.util.ui.JBUI;
|
import com.intellij.util.ui.JBUI;
|
||||||
|
import com.intellij.util.ui.UIUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.intellij.openapi.progress.util.ProgressIndicatorUtils.runInReadActionWithWriteActionPriority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author peter
|
* @author peter
|
||||||
@@ -59,15 +71,25 @@ public abstract class NavigationGutterIconRenderer extends GutterIconRenderer
|
|||||||
private final @PopupContent String myEmptyText;
|
private final @PopupContent String myEmptyText;
|
||||||
protected final Computable<? extends PsiElementListCellRenderer> myCellRenderer;
|
protected final Computable<? extends PsiElementListCellRenderer> myCellRenderer;
|
||||||
private final NotNullLazyValue<? extends List<SmartPsiElementPointer<?>>> myPointers;
|
private final NotNullLazyValue<? extends List<SmartPsiElementPointer<?>>> myPointers;
|
||||||
|
private final boolean myComputeTargetsInBackground;
|
||||||
|
|
||||||
protected NavigationGutterIconRenderer(final @PopupTitle String popupTitle,
|
protected NavigationGutterIconRenderer(@PopupTitle String popupTitle,
|
||||||
final @PopupContent String emptyText,
|
@PopupContent String emptyText,
|
||||||
@NotNull Computable<? extends PsiElementListCellRenderer<?>> cellRenderer,
|
@NotNull Computable<? extends PsiElementListCellRenderer<?>> cellRenderer,
|
||||||
@NotNull NotNullLazyValue<? extends List<SmartPsiElementPointer<?>>> pointers) {
|
@NotNull NotNullLazyValue<? extends List<SmartPsiElementPointer<?>>> pointers) {
|
||||||
|
this(popupTitle, emptyText, cellRenderer, pointers, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected NavigationGutterIconRenderer(@PopupTitle String popupTitle,
|
||||||
|
@PopupContent String emptyText,
|
||||||
|
Computable<? extends PsiElementListCellRenderer> cellRenderer,
|
||||||
|
NotNullLazyValue<? extends List<SmartPsiElementPointer<?>>> pointers,
|
||||||
|
boolean computeTargetsInBackground) {
|
||||||
myPopupTitle = popupTitle;
|
myPopupTitle = popupTitle;
|
||||||
myEmptyText = emptyText;
|
myEmptyText = emptyText;
|
||||||
myCellRenderer = cellRenderer;
|
myCellRenderer = cellRenderer;
|
||||||
myPointers = pointers;
|
myPointers = pointers;
|
||||||
|
myComputeTargetsInBackground = computeTargetsInBackground;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -117,13 +139,43 @@ public abstract class NavigationGutterIconRenderer extends GutterIconRenderer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void navigate(@Nullable final MouseEvent event, @Nullable final PsiElement elt) {
|
public void navigate(@Nullable MouseEvent event, @Nullable PsiElement elt) {
|
||||||
List<PsiElement> list = getTargetElements();
|
if (event != null && myComputeTargetsInBackground && event.getComponent() != null) {
|
||||||
if (list.isEmpty()) {
|
navigateTargetsAsync(event);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
navigateTargets(event, getTargetElements());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateTargetsAsync(@NotNull MouseEvent event) {
|
||||||
|
RelativePoint relativePoint = new RelativePoint(event);
|
||||||
|
Runnable removeIcon = addLoadingIcon(relativePoint);
|
||||||
|
|
||||||
|
AppExecutorUtil.getAppExecutorService().execute(() -> {
|
||||||
|
ProgressManager.getInstance().computePrioritized(() -> {
|
||||||
|
ProgressManager.getInstance().executeProcessUnderProgress(() -> {
|
||||||
|
Ref<List<PsiElement>> targets = Ref.create();
|
||||||
|
boolean success = runInReadActionWithWriteActionPriority(() -> targets.set(getTargetElements()));
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().invokeLater(() -> {
|
||||||
|
removeIcon.run();
|
||||||
|
if (success) {
|
||||||
|
navigateTargets(event, targets.get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, new EmptyProgressIndicator());
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateTargets(@Nullable MouseEvent event, List<PsiElement> targets) {
|
||||||
|
if (targets.isEmpty()) {
|
||||||
if (myEmptyText != null) {
|
if (myEmptyText != null) {
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
final JComponent label = HintUtil.createErrorLabel(myEmptyText);
|
JComponent label = HintUtil.createErrorLabel(myEmptyText);
|
||||||
label.setBorder(JBUI.Borders.empty(2, 7, 2, 7));
|
label.setBorder(JBUI.Borders.empty(2, 7));
|
||||||
JBPopupFactory.getInstance().createBalloonBuilder(label)
|
JBPopupFactory.getInstance().createBalloonBuilder(label)
|
||||||
.setFadeoutTime(3000)
|
.setFadeoutTime(3000)
|
||||||
.setFillColor(HintUtil.getErrorColor())
|
.setFillColor(HintUtil.getErrorColor())
|
||||||
@@ -140,7 +192,7 @@ public abstract class NavigationGutterIconRenderer extends GutterIconRenderer
|
|||||||
protected void navigateToItems(@Nullable MouseEvent event) {
|
protected void navigateToItems(@Nullable MouseEvent event) {
|
||||||
List<Navigatable> navigatables = new ArrayList<>();
|
List<Navigatable> navigatables = new ArrayList<>();
|
||||||
for (SmartPsiElementPointer<?> pointer : myPointers.getValue()) {
|
for (SmartPsiElementPointer<?> pointer : myPointers.getValue()) {
|
||||||
ContainerUtil.addIfNotNull(navigatables, getNavigatable(pointer));
|
ContainerUtil.addIfNotNull(navigatables, getNavigatable(pointer));
|
||||||
}
|
}
|
||||||
if (navigatables.size() == 1) {
|
if (navigatables.size() == 1) {
|
||||||
navigatables.get(0).navigate(true);
|
navigatables.get(0).navigate(true);
|
||||||
@@ -173,4 +225,30 @@ public abstract class NavigationGutterIconRenderer extends GutterIconRenderer
|
|||||||
final PsiElement navigationElement = element.getNavigationElement();
|
final PsiElement navigationElement = element.getNavigationElement();
|
||||||
return navigationElement instanceof Navigatable ? (Navigatable)navigationElement : null;
|
return navigationElement instanceof Navigatable ? (Navigatable)navigationElement : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @NotNull Runnable addLoadingIcon(@Nullable RelativePoint point) {
|
||||||
|
JRootPane rootPane = point == null ? null : UIUtil.getRootPane(point.getComponent());
|
||||||
|
JComponent glassPane = rootPane == null ? null : (JComponent)rootPane.getGlassPane();
|
||||||
|
if (glassPane == null) return () -> {};
|
||||||
|
|
||||||
|
JLabel icon = new JLabel(AnimatedIcon.Big.INSTANCE);
|
||||||
|
Dimension size = icon.getPreferredSize();
|
||||||
|
icon.setSize(size);
|
||||||
|
Point location = point.getPoint(glassPane);
|
||||||
|
location.x -= size.width / 2;
|
||||||
|
location.y -= size.height / 2;
|
||||||
|
icon.setLocation(location);
|
||||||
|
EdtScheduledExecutorService.getInstance().schedule(() -> {
|
||||||
|
if (!icon.isVisible()) return;
|
||||||
|
glassPane.add(icon);
|
||||||
|
}, 500, TimeUnit.MILLISECONDS);
|
||||||
|
return () -> {
|
||||||
|
if (icon.getParent() != null) {
|
||||||
|
glassPane.remove(icon);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
icon.setVisible(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user