AsyncPopupStep to prepare real PopupStep in background

This commit is contained in:
Vassiliy.Kudryashov
2017-10-05 20:55:27 +03:00
parent 8208fe5475
commit f96956b88c
6 changed files with 206 additions and 4 deletions
@@ -0,0 +1,8 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.popup;
import com.intellij.openapi.ui.popup.PopupStep;
public interface NextStepHandler {
void handleNextStep(final PopupStep nextStep, Object parentValue);
}
@@ -25,6 +25,8 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.ui.PopupBorder;
import com.intellij.ui.ScreenUtil;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.popup.async.AsyncPopupImpl;
import com.intellij.ui.popup.async.AsyncPopupStep;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.ui.popup.tree.TreePopupImpl;
import com.intellij.ui.popup.util.MnemonicsSearch;
@@ -394,6 +396,9 @@ public abstract class WizardPopup extends AbstractPopup implements ActionListene
}
protected WizardPopup createPopup(WizardPopup parent, PopupStep step, Object parentValue) {
if (step instanceof AsyncPopupStep) {
return new AsyncPopupImpl(parent, (AsyncPopupStep)step, parentValue);
}
if (step instanceof ListPopupStep) {
return new ListPopupImpl(parent, (ListPopupStep)step, parentValue);
}
@@ -0,0 +1,111 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.popup.async;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.popup.NextStepHandler;
import com.intellij.ui.popup.WizardPopup;
import com.intellij.util.Alarm;
import com.intellij.util.ui.AsyncProcessIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class AsyncPopupImpl extends WizardPopup implements Runnable {
private final Future<PopupStep> myFuture;
private final Object myParentValue;
private final NextStepHandler myCallBackParent;
private final Alarm myAlarm;
private JPanel myPanel;
public AsyncPopupImpl(@Nullable WizardPopup parent, @NotNull AsyncPopupStep step, Object parentValue) {
super(parent, step);
if (!(parent instanceof NextStepHandler)) throw new IllegalArgumentException("parent must be NextStepHandler");
myCallBackParent = (NextStepHandler)parent;
myParentValue = parentValue;
myFuture = ApplicationManager.getApplication().executeOnPooledThread(step);
myAlarm = new Alarm(this);
myAlarm.addRequest(this, 200);
Disposer.register(this, new Disposable() {
@Override
public void dispose() {
if (!myFuture.isCancelled() && !myFuture.isDone()) {
myFuture.cancel(false);
}
}
});
}
@Override
public void run() {
if (myFuture.isCancelled()) return;
if (myFuture.isDone()) {
goBack();
try {
myCallBackParent.handleNextStep(myFuture.get(), myParentValue);
}
catch (InterruptedException e) {
//ignore
}
catch (ExecutionException e) {
//ignore
}
return;
}
myAlarm.addRequest(this, 200);
}
@Override
protected JComponent createContent() {
if (myPanel != null) return myPanel;
myPanel = new JPanel(new BorderLayout());
myPanel.add(new AsyncProcessIcon("Async Popup Step"), BorderLayout.WEST);
myPanel.add(new JBLabel("Loading..."), BorderLayout.CENTER);
myPanel.setBorder(new EmptyBorder(UIUtil.getListCellPadding()));
myPanel.setBackground(UIUtil.getListSelectionBackground());
myPanel.registerKeyboardAction(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
goBack();
}
}, KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), JComponent.WHEN_FOCUSED);
return myPanel;
}
@Override
protected JComponent getPreferredFocusableComponent() {
return createContent();
}
@Override
protected InputMap getInputMap() {
return null;
}
@Override
protected ActionMap getActionMap() {
return null;
}
@Override
protected void onChildSelectedFor(Object value) {
}
}
@@ -0,0 +1,72 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ui.popup.async;
import com.intellij.openapi.ui.popup.MnemonicNavigationFilter;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.SpeedSearchFilter;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.Callable;
/**
* Use this "mock" popup step when you need long processing (I/O, network activity etc.) to build real one.
* The real example is a list of processes on remote host you can connect to with debugger.
* This step would be started as 'callable' on pooled thread as soon as AsyncPopupImpl instance with "Loading..." text is being created
* When real popup step is obtained from the background task, mock item would be automatically replaced with it.
*/
public abstract class AsyncPopupStep implements PopupStep, Callable<PopupStep> {
@Nullable
@Override
public String getTitle() {
return null;
}
@Nullable
@Override
public PopupStep onChosen(Object selectedValue, boolean finalChoice) {
return null;
}
@Override
public boolean hasSubstep(Object selectedValue) {
return false;
}
@Override
public void canceled() {
}
@Override
public boolean isMnemonicsNavigationEnabled() {
return false;
}
@Nullable
@Override
public MnemonicNavigationFilter getMnemonicNavigationFilter() {
return null;
}
@Override
public boolean isSpeedSearchEnabled() {
return false;
}
@Nullable
@Override
public SpeedSearchFilter getSpeedSearchFilter() {
return null;
}
@Override
public boolean isAutoSelectionEnabled() {
return false;
}
@Nullable
@Override
public Runnable getFinalRunnable() {
return null;
}
}
@@ -34,6 +34,7 @@ import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBList;
import com.intellij.ui.popup.ClosableByLeftArrow;
import com.intellij.ui.popup.HintUpdateSupply;
import com.intellij.ui.popup.NextStepHandler;
import com.intellij.ui.popup.WizardPopup;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
@@ -47,7 +48,7 @@ import java.awt.event.*;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
public class ListPopupImpl extends WizardPopup implements ListPopup {
public class ListPopupImpl extends WizardPopup implements ListPopup, NextStepHandler {
private static final Logger LOG = Logger.getInstance("#com.intellij.ui.popup.list.ListPopupImpl");
private MyList myList;
@@ -421,7 +422,11 @@ public class ListPopupImpl extends WizardPopup implements ListPopup {
}
}
private boolean handleNextStep(final PopupStep nextStep, Object parentValue, InputEvent e) {
public void handleNextStep(final PopupStep nextStep, Object parentValue) {
handleNextStep(nextStep, parentValue, null);
}
public boolean handleNextStep(final PopupStep nextStep, Object parentValue, InputEvent e) {
if (nextStep != PopupStep.FINAL_CHOICE) {
final Point point = myList.indexToLocation(myList.getSelectedIndex());
SwingUtilities.convertPointToScreen(point, myList);
@@ -27,6 +27,7 @@ import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.TreePopup;
import com.intellij.openapi.ui.popup.TreePopupStep;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.popup.NextStepHandler;
import com.intellij.ui.popup.WizardPopup;
import com.intellij.ui.treeStructure.SimpleTree;
import com.intellij.ui.treeStructure.filtered.FilteringTreeBuilder;
@@ -46,7 +47,7 @@ import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class TreePopupImpl extends WizardPopup implements TreePopup {
public class TreePopupImpl extends WizardPopup implements TreePopup, NextStepHandler {
private static final Logger LOG = Logger.getInstance("#com.intellij.ui.popup.tree.TreePopupImpl");
private MyTree myWizardTree;
@@ -382,7 +383,7 @@ public class TreePopupImpl extends WizardPopup implements TreePopup {
}
}
private void handleNextStep(PopupStep nextStep, Object parentValue) {
public void handleNextStep(PopupStep nextStep, Object parentValue) {
final Rectangle pathBounds = myWizardTree.getPathBounds(myWizardTree.getSelectionPath());
final Point point = new RelativePoint(myWizardTree, new Point(getContent().getWidth() + 2, (int) pathBounds.getY())).getScreenPoint();
myChild = createPopup(this, nextStep, parentValue);