fix completion extender blinking

This commit is contained in:
Konstantin Bulenkov
2012-06-06 09:44:53 +02:00
parent 5176f2edcc
commit 041e6f9a12
4 changed files with 115 additions and 147 deletions
@@ -15,122 +15,56 @@
*/
package com.intellij.codeInsight.lookup.impl;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.HintHint;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.ScreenUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.ui.UIUtil;
import com.intellij.ui.ListExpandableItemsHandler;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.*;
/**
* @author Konstantin Bulenkov
*/
public class CompletionExtender extends LightweightHint {
private LookupElement myElement;
private LookupImpl myLookup;
private int myIndex;
public class CompletionExtender extends ListExpandableItemsHandler {
public CompletionExtender(@NotNull LookupElement element, @NotNull LookupImpl lookup) {
super(createComponent(element, lookup));
setForceHideShadow(true);
myElement = element;
myLookup = lookup;
myIndex = myLookup.getList().getSelectedIndex();
myLookup.getComponent().addComponentListener(new ComponentAdapter() {
public CompletionExtender(@NotNull final JList list) {
super(list);
list.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
recalculateLocation();
}
public void componentShown(ComponentEvent e) {
if (myComponent.getParent() != null && myComponent.getParent().getParent() instanceof JScrollPane) {
final JScrollBar verticalScrollBar = ((JScrollPane)myComponent.getParent().getParent()).getVerticalScrollBar();
final JScrollBar horizontalScrollBar = ((JScrollPane)myComponent.getParent().getParent()).getVerticalScrollBar();
final AdjustmentListener listener = new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
updateCurrentSelection();
}
};
if (verticalScrollBar != null) verticalScrollBar.addAdjustmentListener(listener);
if (horizontalScrollBar != null) horizontalScrollBar.addAdjustmentListener(listener);
@Override
public void componentMoved(ComponentEvent e) {
recalculateLocation();
list.removeComponentListener(this);
}
}
});
}
public LookupElement getLookupElement() {
return myElement;
}
public boolean sameAsFor(LookupElement item) {
return getLookupElement().equals(item)
&& myIndex == myLookup.getList().getSelectedIndex();
}
private static JComponent createComponent(LookupElement element, LookupImpl lookup) {
final LookupCellRenderer renderer = ((LookupCellRenderer)lookup.getList().getCellRenderer()).createExtenderRenderer();
final JComponent component = (JComponent)renderer.getListCellRendererComponent(lookup.getList(), element,
lookup.getList().getSelectedIndex(),
true, false);
component.setSize(component.getPreferredSize());
return component;
}
public boolean show() {
if (SystemInfo.isUnix) {
//TODO[kb]: fix shadow. This hint is always heavyweight window and has a shadow
hide();
return false;
}
if (!checkComponentBounds()) {
hide();
return false;
}
final JList list = myLookup.getList();
if (getComponent().getWidth() > list.getWidth()) {
final JComponent rootPane = myLookup.myLayeredPane;
final Point p = list.getLocationOnScreen();
p.y += list.indexToLocation(list.getSelectedIndex()).y;
SwingUtilities.convertPointFromScreen(p, rootPane);
if (rootPane != null) {
final HintHint hint = new HintHint();
show(rootPane, p.x, p.y, null, hint);
return true;
}
}
return false;
}
private boolean checkComponentBounds() {
final Dimension size = getComponent().getPreferredSize();
final JList list = myLookup.getList();
final Point p = list.getLocationOnScreen();
p.y += list.indexToLocation(list.getSelectedIndex()).y;
final Rectangle rectangle = new Rectangle(p, size);
return !ScreenUtil.isOutsideOnTheRightOFScreen(rectangle);
}
void recalculateLocation() {
if (!isVisible()) return;
final JList list = myLookup.getList();
final Point p = list.getLocationOnScreen();
final Point point = list.indexToLocation(list.getSelectedIndex());
if (point != null) {
p.y += point.y;
final JComponent rootPane = UIUtil.getRootPane(myLookup.getEditor().getContentComponent());
if (rootPane != null) {
SwingUtilities.convertPointFromScreen(p, rootPane);
setLocation(new RelativePoint(rootPane, p));
return;
}
}
hide();
@Override
protected void handleSelectionChange(Integer selected, boolean processIfUnfocused) {
super.handleSelectionChange(myComponent.getSelectedIndex(), true);
}
@Override
public void hide() {
super.hide();
myLookup = null;
myElement = null;
protected void onFocusLost() {
//don't hide hint
}
@Override
protected void handleMouseEvent(MouseEvent e, boolean forceUpdate) {
// don't show or hide hint on mouse events
}
@Override
protected boolean isPaintBorder() {
return false;
}
}
@@ -37,6 +37,8 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
/**
* @author peter
@@ -68,15 +70,13 @@ public class LookupCellRenderer implements ListCellRenderer {
private final SimpleColoredComponent myNameComponent;
private final SimpleColoredComponent myTailComponent;
private final SimpleColoredComponent myTypeLabel;
private final JPanel myPanel;
private final LookupPanel myPanel;
private final Map<Integer, Boolean> mySelected = new HashMap<Integer, Boolean>();
private static final String ELLIPSIS = "\u2026";
private final boolean myFullSize;
private int myMaxWidth = -1;
public LookupCellRenderer(LookupImpl lookup, boolean fullSize) {
myFullSize = fullSize;
public LookupCellRenderer(LookupImpl lookup) {
EditorColorsScheme scheme = lookup.getEditor().getColorsScheme();
myNormalFont = scheme.getFont(EditorFontType.PLAIN);
myBoldFont = scheme.getFont(EditorFontType.BOLD);
@@ -124,7 +124,7 @@ public class LookupCellRenderer implements ListCellRenderer {
final Color background = isSelected ? SELECTED_BACKGROUND_COLOR : BACKGROUND_COLOR;
int allowedWidth = list.getWidth() - AFTER_TAIL - AFTER_TYPE - getIconIndent();
final LookupElementPresentation presentation = new RealLookupElementPresentation(myFullSize ? getMaxWidth() : allowedWidth, myNormalMetrics, myBoldMetrics, myLookup);
final LookupElementPresentation presentation = new RealLookupElementPresentation(isSelected ? getMaxWidth() : allowedWidth, myNormalMetrics, myBoldMetrics, myLookup);
if (item.isValid()) {
item.renderElement(presentation);
} else {
@@ -139,13 +139,37 @@ public class LookupCellRenderer implements ListCellRenderer {
myTypeLabel.clear();
if (allowedWidth > 0) {
allowedWidth -= setTypeTextLabel(item, background, foreground, presentation, myFullSize ? getMaxWidth() : allowedWidth, isSelected);
allowedWidth -= setTypeTextLabel(item, background, foreground, presentation, isSelected ? getMaxWidth() : allowedWidth, isSelected);
}
myTailComponent.clear();
myTailComponent.setBackground(background);
if (myFullSize || allowedWidth >= 0) {
setTailTextLabel(isSelected, presentation, foreground, myFullSize ? getMaxWidth() : allowedWidth);
if (isSelected || allowedWidth >= 0) {
setTailTextLabel(isSelected, presentation, foreground, isSelected ? getMaxWidth() : allowedWidth);
}
if (mySelected.containsKey(index)) {
if (!isSelected && mySelected.get(index)) {
myPanel.setUpdateExtender(true);
}
}
mySelected.put(index, isSelected);
final double w = myNameComponent.getPreferredSize().getWidth() +
myTailComponent.getPreferredSize().getWidth() +
myTypeLabel.getPreferredSize().getWidth();
myPanel.removeAll();
if (isSelected && w > list.getWidth()) {
myPanel.setLayout(new BoxLayout(myPanel, BoxLayout.X_AXIS));
myPanel.add(myNameComponent);
myPanel.add(myTailComponent);
myPanel.add(myTypeLabel);
} else {
myPanel.setLayout(new BorderLayout());
myPanel.add(myNameComponent, BorderLayout.WEST);
myPanel.add(myTailComponent, BorderLayout.CENTER);
myPanel.add(myTypeLabel, BorderLayout.EAST);
}
return myPanel;
@@ -327,12 +351,6 @@ public class LookupCellRenderer implements ListCellRenderer {
return RealLookupElementPresentation.calculateWidth(p, myNormalMetrics, myBoldMetrics) + AFTER_TAIL + AFTER_TYPE;
}
public LookupCellRenderer createExtenderRenderer() {
LookupCellRenderer renderer = new LookupCellRenderer(myLookup, true);
renderer.myEmptyIcon = myEmptyIcon;
return renderer;
}
public int getIconIndent() {
return myNameComponent.getIconTextGap() + myEmptyIcon.getIconWidth();
}
@@ -350,15 +368,23 @@ public class LookupCellRenderer implements ListCellRenderer {
}
private class LookupPanel extends JPanel {
boolean myUpdateExtender;
public LookupPanel() {
super(new BorderLayout());
}
public void setUpdateExtender(boolean updateExtender) {
myUpdateExtender = updateExtender;
}
public void paint(Graphics g){
if (!myLookup.isFocused() && myLookup.isCompletion()) {
((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f));
}
super.paint(g);
//if (myUpdateExtender) {
// myLookup.updateExtender();
//}
}
}
}
@@ -41,6 +41,7 @@ import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.*;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopup;
@@ -56,9 +57,7 @@ import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.ListScrollingUtil;
import com.intellij.ui.ScreenUtil;
import com.intellij.ui.*;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBScrollPane;
@@ -97,12 +96,11 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
private final Project myProject;
private final Editor myEditor;
private CompletionExtender extender;
private String myInitialPrefix;
private boolean myStableStart;
private RangeMarker myLookupStartMarker;
private final JList myList = new JBList(new DefaultListModel()) {
private final JBList myList = new JBList(new DefaultListModel()) {
@Override
protected void processKeyEvent(final KeyEvent e) {
final char keyChar = e.getKeyChar();
@@ -118,6 +116,13 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
super.processKeyEvent(e);
}
ExpandableItemsHandler<Integer> myExtender = new CompletionExtender(this);
@NotNull
@Override
public ExpandableItemsHandler<Integer> getExpandableItemsHandler() {
return myExtender;
}
};
private final LookupCellRenderer myCellRenderer;
private Boolean myPositionedAbove = null;
@@ -165,7 +170,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
myPresentableArranger = arranger;
myIconPanel.setVisible(false);
myCellRenderer = new LookupCellRenderer(this, false);
myCellRenderer = new LookupCellRenderer(this);
myList.setCellRenderer(myCellRenderer);
myList.setFocusable(false);
@@ -174,6 +179,8 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
myList.setBackground(LookupCellRenderer.BACKGROUND_COLOR);
myList.getExpandableItemsHandler();
myScrollBarIncreaseButton = new JButton();
myScrollBarIncreaseButton.setFocusable(false);
myScrollBarIncreaseButton.setRequestFocusEnabled(false);
@@ -188,7 +195,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
return myScrollBarIncreaseButton;
}
});
getComponent().add(myLayeredPane, BorderLayout.CENTER);
//IDEA-82111
@@ -886,16 +892,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
updateHint(item);
}
oldItem = item;
if (item != null && LookupImpl.this.isVisible() && LookupImpl.this.isFocused()) {
if (extender == null || !extender.isVisible() || !extender.sameAsFor(item)) {
if (extender != null) extender.hide();
extender = new CompletionExtender(item, LookupImpl.this);
if (!extender.show()) {
extender.hide();
}
}
}
}
});
@@ -1231,9 +1227,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
Disposer.dispose(myProcessIcon);
Disposer.dispose(myHintAlarm);
if (extender != null) {
extender.hide();
}
myDisposed = true;
disposeTrace = DebugUtil.currentStackTrace() + "\n============";
//noinspection AssignmentToStaticFieldFromInstanceMethod
@@ -1458,5 +1451,4 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
return myPresentableArranger.getRelevanceStrings();
}
}
}
@@ -89,7 +89,7 @@ abstract public class AbstractExpandableItemsHandler<KeyType, ComponentType exte
myComponent.addFocusListener(
new FocusAdapter() {
public void focusLost(FocusEvent e) {
hideHint();
onFocusLost();
}
public void focusGained(FocusEvent e) {
@@ -134,6 +134,12 @@ abstract public class AbstractExpandableItemsHandler<KeyType, ComponentType exte
}
}
);
}
protected void onFocusLost() {
hideHint();
}
@Override
@@ -160,7 +166,7 @@ abstract public class AbstractExpandableItemsHandler<KeyType, ComponentType exte
handleMouseEvent(e, true);
}
private void handleMouseEvent(MouseEvent e, boolean forceUpdate) {
protected void handleMouseEvent(MouseEvent e, boolean forceUpdate) {
KeyType selected = getCellKeyForPoint(e.getPoint());
if (forceUpdate || !Comparing.equal(myKey, selected)) {
handleSelectionChange(selected, true);
@@ -307,14 +313,16 @@ abstract public class AbstractExpandableItemsHandler<KeyType, ComponentType exte
g.translate(-(visibleRect.x + visibleRect.width - cellBounds.x), 0);
doPaintTooltipImage(renderer, cellBounds, height, g, key);
// paint border
g.translate((visibleRect.x + visibleRect.width - cellBounds.x), 0);
g.setColor(Color.GRAY);
int rightX = myImage.getWidth() - 1;
final int h = myImage.getHeight();
UIUtil.drawLine(g, 0, 0, rightX, 0);
UIUtil.drawLine(g, rightX, 0, rightX, h);
UIUtil.drawLine(g, 0, h - 1, rightX, h - 1);
if (isPaintBorder()) {
g.translate((visibleRect.x + visibleRect.width - cellBounds.x), 0);
g.setColor(getBorderColor());
int rightX = myImage.getWidth() - 1;
final int h = myImage.getHeight();
UIUtil.drawLine(g, 0, 0, rightX, 0);
UIUtil.drawLine(g, rightX, 0, rightX, h);
UIUtil.drawLine(g, 0, h - 1, rightX, h - 1);
}
g.dispose();
myRendererPane.remove(renderer);
@@ -322,6 +330,14 @@ abstract public class AbstractExpandableItemsHandler<KeyType, ComponentType exte
return new Point(visibleRect.x + visibleRect.width, cellBounds.y);
}
protected boolean isPaintBorder() {
return true;
}
protected Color getBorderColor() {
return Color.GRAY;
}
protected BufferedImage createImage(final int height, final int width) {
return new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
}