new NavBar popup (in development)

This commit is contained in:
Konstantin Bulenkov
2011-03-09 16:09:20 +03:00
parent d7e0a953e0
commit f143899246
5 changed files with 272 additions and 87 deletions
@@ -15,12 +15,16 @@
*/
package com.intellij.ide.navigationToolbar;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.psi.PsiElement;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.util.Icons;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import javax.swing.*;
import java.awt.*;
@@ -28,18 +32,20 @@ import java.awt.*;
/**
* @author Konstantin Bulenkov
*/
class NavBarItem extends SimpleColoredComponent {
class NavBarItem extends SimpleColoredComponent implements DataProvider{
private final String myText;
private final SimpleTextAttributes myAttributes;
private final int myIndex;
private final Icon myIcon;
private final NavBarPanel myPanel;
private Object myObject;
private final boolean isPopupElement;
public NavBarItem(NavBarPanel panel, Object object, int idx) {
myPanel = panel;
myObject = object;
myIndex = idx;
isPopupElement = idx == -1;
if (object != null) {
Icon closedIcon = NavBarPresentation.getIcon(object, false);
Icon openIcon = NavBarPresentation.getIcon(object, true);
@@ -63,6 +69,15 @@ class NavBarItem extends SimpleColoredComponent {
update();
}
/**
* item for node popup
* @param panel
* @param object
*/
public NavBarItem(NavBarPanel panel, Object object) {
this(panel, object, -1);
}
public Object getObject() {
return myObject;
}
@@ -75,12 +90,13 @@ class NavBarItem extends SimpleColoredComponent {
clear();
setIcon(myIcon);
boolean focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner() == myPanel;
final Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
boolean focused = isPopupElement ? myPanel.isNodePopupActive() : focusOwner == myPanel;
final NavBarModel model = myPanel.getModel();
boolean selected = model.getSelectedIndex() == myIndex;
boolean selected = isPopupElement ? myPanel.isSelectedInPopup(myObject) : model.getSelectedIndex() == myIndex;
setPaintFocusBorder(!focused && selected);
setPaintFocusBorder(!focused && selected && !isPopupElement);
setFocusBorderAroundIcon(false);
setBackground(selected && focused
@@ -103,8 +119,7 @@ class NavBarItem extends SimpleColoredComponent {
private Icon wrapIcon(final Icon openIcon, final Icon closedIcon, final int idx) {
return new Icon() {
public void paintIcon(Component c, Graphics g, int x, int y) {
final ListPopupImpl nodePopup = myPanel.getNodePopup();
if (myPanel.getModel().getSelectedIndex() == idx && nodePopup != null && nodePopup.isVisible()) {
if (myPanel.getModel().getSelectedIndex() == idx && myPanel.isNodePopupActive()) {
openIcon.paintIcon(c, g, x, y);
}
else {
@@ -121,4 +136,21 @@ class NavBarItem extends SimpleColoredComponent {
}
};
}
@Override
public Object getData(@NonNls String dataId) {
if (PlatformDataKeys.PROJECT.is(dataId)) {
return myPanel.getProject();
}
if (LangDataKeys.PSI_ELEMENT.is(dataId)) {
return myObject instanceof PsiElement ? myObject : null;
}
if (LangDataKeys.PSI_FILE.is(dataId)) {
return myObject instanceof PsiElement ? ((PsiElement)myObject).getContainingFile() : null;
}
return null;
}
}
@@ -27,7 +27,6 @@ import com.intellij.problems.WolfTheProblemSolver;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiTreeChangeEvent;
import com.intellij.psi.PsiTreeChangeListener;
import com.intellij.ui.popup.list.ListPopupImpl;
import com.intellij.util.messages.MessageBusConnection;
import org.jetbrains.annotations.NotNull;
@@ -142,8 +141,7 @@ public class NavBarListener extends WolfTheProblemSolver.ProblemListener
}
private void processFocusLost(FocusEvent e) {
final ListPopupImpl nodePopup = myPanel.getNodePopup();
final boolean nodePopupInactive = nodePopup == null || !nodePopup.isVisible() || !nodePopup.isFocused();
final boolean nodePopupInactive = !myPanel.isNodePopupActive();
boolean childPopupInactive = !JBPopupFactory.getInstance().isChildPopupFocused(myPanel);
if (nodePopupInactive && childPopupInactive) {
final Component opposite = e.getOppositeComponent();
@@ -37,6 +37,7 @@ import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.AsyncResult;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
@@ -66,7 +67,7 @@ import java.util.Set;
* @author Konstantin Bulenkov
* @author Anna Kozlova
*/
public class NavBarPanel extends OpaquePanel.List implements DataProvider, PopupOwner, Disposable{
public class NavBarPanel extends OpaquePanel.List implements DataProvider, PopupOwner, Disposable {
private final NavBarModel myModel;
@@ -79,8 +80,10 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
private final IdeView myIdeView;
private final CopyPasteDelegator myCopyPasteDelegator;
private LightweightHint myHint = null;
private NavBarPopup myNodeHint = null;
private ListPopupImpl myNodePopup = null;
private JComponent myHintContainer;
private Component myContextComponent;
@@ -114,8 +117,9 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
Disposer.register(project, this);
}
public ListPopupImpl getNodePopup() {
return myNodePopup;
public boolean isNodePopupActive() {
return (myNodePopup != null && myNodePopup.isVisible() && myNodePopup.isFocused())
|| (myNodeHint != null && myNodeHint.isVisible() && myNodeHint.getComponent().hasFocus());
}
public LightweightHint getHint() {
@@ -194,6 +198,13 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
return myDisposed;
}
boolean isSelectedInPopup(Object object) {
if (isNodePopupActive()) {
return myNodeHint.getSelectedValue() == object;
}
return false;
}
private static Object optimizeTarget(Object target) {
if (target instanceof PsiDirectory && ((PsiDirectory)target).getFiles().length == 0) {
final PsiDirectory[] subDir = ((PsiDirectory)target).getSubdirectories();
@@ -234,7 +245,7 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
public void moveRight() {
shiftFocus(1);
}
private void shiftFocus(int direction) {
void shiftFocus(int direction) {
final int selectedIndex = myModel.getSelectedIndex();
final int index = myModel.getIndexByModel(selectedIndex + direction);
myModel.setSelectedIndex(index);
@@ -402,76 +413,63 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
icons[i] = NavBarPresentation.getIcon(siblings[i], false);
}
final NavBarItem item = getItem(index);
final BaseListPopupStep<Object> step = new BaseListPopupStep<Object>("", siblings, icons) {
public boolean isSpeedSearchEnabled() {
return true;
final BaseListPopupStep<Object> step;
if (Registry.is("navbar.newpopup")) {
myNodePopup = null;
myNodeHint = new NavBarPopup(this, siblings, index < myModel.size() - 1 ? objects.indexOf(myModel.getElement(index + 1)) : 0);
if (item != null && item.isShowing()) {
myNodeHint.show(item);
}
@NotNull
public String getTextFor(final Object value) {
return NavBarPresentation.getPresentableText(value, null);
}
public boolean isSelectable(Object value) {
return true;
}
public PopupStep onChosen(final Object selectedValue, final boolean finalChoice) {
return doFinalStep(new Runnable() {
public void run() {
navigateInsideBar(optimizeTarget(selectedValue));
}
});
}
};
step.setDefaultOptionIndex(index < myModel.size() - 1 ? objects.indexOf(myModel.getElement(index + 1)) : 0);
myNodePopup = new ListPopupImpl(step) {
protected ListCellRenderer getListElementRenderer() {
return new NavBarListCellRenderer(myProject, NavBarPanel.this);
}
@Override
public void cancel(InputEvent e) {
super.cancel(e);
}
};
myNodePopup.registerAction("left", KeyEvent.VK_LEFT, 0, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
myNodePopup.goBack();
shiftFocus(-1);
restorePopup();
}
});
myNodePopup.registerAction("right", KeyEvent.VK_RIGHT, 0, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
myNodePopup.goBack();
shiftFocus(1);
restorePopup();
}
});
ListenerUtil.addMouseListener(myNodePopup.getComponent(), new MouseAdapter() {
public void mouseReleased(final MouseEvent e) {
if (SystemInfo.isWindows) {
click(e);
} else {
myNodeHint = null;
step = new BaseListPopupStep<Object>("", siblings, icons) {
public boolean isSpeedSearchEnabled() {
return true;
}
}
public void mousePressed(final MouseEvent e) {
if (!SystemInfo.isWindows) {
click(e);
@NotNull
public String getTextFor(final Object value) {
return NavBarPresentation.getPresentableText(value, null);
}
}
private void click(final MouseEvent e) {
if (!e.isConsumed() && e.isPopupTrigger()) {
myModel.setSelectedIndex(index);
IdeFocusManager.getInstance(myProject).requestFocus(NavBarPanel.this, true);
rightClick(index);
e.consume();
public boolean isSelectable(Object value) {
return true;
}
}
});
public PopupStep onChosen(final Object selectedValue, final boolean finalChoice) {
return doFinalStep(new Runnable() {
public void run() {
navigateInsideBar(optimizeTarget(selectedValue));
}
});
}
};
step.setDefaultOptionIndex(index < myModel.size() - 1 ? objects.indexOf(myModel.getElement(index + 1)) : 0);
myNodePopup = new ListPopupImpl(step) {
protected ListCellRenderer getListElementRenderer() {
return new NavBarListCellRenderer(myProject, NavBarPanel.this);
}
@Override
public void cancel(InputEvent e) {
super.cancel(e);
}
};
myNodePopup.registerAction("left", KeyEvent.VK_LEFT, 0, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
myNodePopup.goBack();
shiftFocus(-1);
restorePopup();
}
});
myNodePopup.registerAction("right", KeyEvent.VK_RIGHT, 0, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
myNodePopup.goBack();
shiftFocus(1);
restorePopup();
}
});
if (!isValid()) {
validate();
@@ -480,11 +478,13 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
if (item != null && item.isShowing() && step.getValues().size() > 0) {
myNodePopup.showUnderneathOf(item);
}
}
}
}
boolean isNodePopupShowing() {
return myNodePopup != null && myNodePopup.isVisible();
return (myNodePopup != null && myNodePopup.isVisible())
|| (myNodeHint != null && myNodeHint.isVisible());
}
private void navigateInsideBar(final Object object) {
@@ -512,7 +512,7 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
}, NavBarUpdateQueue.ID.NAVIGATE_INSIDE);
}
private void rightClick(final int index) {
void rightClick(final int index) {
final ActionManager actionManager = ActionManager.getInstance();
final ActionGroup group = (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(IdeActions.GROUP_NAVBAR_POPUP);
final ActionPopupMenu popupMenu = actionManager.createActionPopupMenu(ActionPlaces.NAVIGATION_BAR, group);
@@ -522,12 +522,16 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
}
}
private void restorePopup() {
void restorePopup() {
cancelPopup();
ctrlClick(myModel.getSelectedIndex());
}
private void cancelPopup() {
void cancelPopup() {
if (myNodeHint != null) {
myNodeHint.hide();
myNodeHint = null;
}
if (myNodePopup != null) {
myNodePopup.cancel();
myNodePopup = null;
@@ -535,6 +539,7 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
}
void hideHint() {
cancelPopup();
if (myHint != null) {
myHint.hide();
myHint = null;
@@ -612,15 +617,18 @@ public class NavBarPanel extends OpaquePanel.List implements DataProvider, Popup
@Nullable
@SuppressWarnings({"unchecked"})
<T> T getSelectedElement(Class<T> klass) {
Object selectedValue1 = myModel.getSelectedValue();
if (selectedValue1 == null) {
Object value = null;
if (myNodeHint != null) {
value = myNodeHint.getSelectedValue();
}
if (value == null) value = myModel.getSelectedValue();
if (value == null) {
final int modelSize = myModel.size();
if (modelSize > 0) {
selectedValue1 = myModel.getElement(modelSize - 1);
value = myModel.getElement(modelSize - 1);
}
}
final Object selectedValue = selectedValue1;
return selectedValue != null && klass.isAssignableFrom(selectedValue.getClass()) ? (T)selectedValue : null;
return value != null && klass.isAssignableFrom(value.getClass()) ? (T)value : null;
}
public Point getBestPopupPosition() {
@@ -0,0 +1,146 @@
/*
* Copyright 2000-2011 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.ide.navigationToolbar;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.HintHint;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.LightweightHint;
import com.intellij.ui.ListenerUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBList;
import com.intellij.util.NotNullFunction;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* @author Konstantin Bulenkov
*/
public class NavBarPopup extends LightweightHint {
private final NavBarPanel myPanel;
public NavBarPopup(NavBarPanel panel, Object[] siblings, final int selectedIndex) {
super(createPopupContent(panel, siblings, selectedIndex));
myPanel = panel;
setFocusRequestor(getComponent());
setForceShowAsPopup(true);
ListenerUtil.addMouseListener(getComponent(), new MouseAdapter() {
public void mouseReleased(final MouseEvent e) {
if (SystemInfo.isWindows) {
click(e);
}
}
public void mousePressed(final MouseEvent e) {
if (!SystemInfo.isWindows) {
click(e);
}
}
private void click(final MouseEvent e) {
if (!e.isConsumed() && e.isPopupTrigger()) {
myPanel.getModel().setSelectedIndex(selectedIndex);
IdeFocusManager.getInstance(myPanel.getProject()).requestFocus(myPanel, true);
myPanel.rightClick(selectedIndex);
e.consume();
}
}
});
}
public void show(final NavBarItem item) {
final RelativePoint point = new RelativePoint(item, new Point(0, item.getHeight()));
final Point p = point.getPoint(myPanel);
show(myPanel, p.x, p.y, myPanel, new HintHint(myPanel, p));
}
private static JBList createPopupContent(final NavBarPanel panel, Object[] siblings, int selectedIndex) {
final JBList list = new JBList(siblings);
list.setDataProvider(new DataProvider() {
@Override
public Object getData(@NonNls String dataId) {
return panel.getData(dataId);
}
});
list.installCellRenderer(new NotNullFunction<Object, JComponent>() {
@NotNull
@Override
public JComponent fun(Object obj) {
return new NavBarItem(panel, obj);
}
});
list.setBorder(IdeBorderFactory.createEmptyBorder(5,5,5,5));
list.setSelectedIndex(selectedIndex);
list.registerKeyboardAction(createMoveAction(panel, -1), KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), JComponent.WHEN_FOCUSED);
list.registerKeyboardAction(createMoveAction(panel, 1), KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), JComponent.WHEN_FOCUSED);
list.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
panel.cancelPopup();
}
});
return list;
}
public Object getSelectedValue() {
return ((JBList)getComponent()).getSelectedValue();
}
private static Action createMoveAction(final NavBarPanel panel, final int direction) {
return new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
panel.cancelPopup();
panel.shiftFocus(direction);
panel.restorePopup();
}
};
}
private static class CancelNavBarPopup extends AbstractAction implements FocusListener {
private final NavBarPanel myPanel;
private CancelNavBarPopup(NavBarPanel panel) {
myPanel = panel;
}
@Override
public void actionPerformed(ActionEvent e) {
cancelPopup();
}
@Override
public void focusGained(FocusEvent e) {
}
@Override
public void focusLost(FocusEvent e) {
cancelPopup();
}
private void cancelPopup() {
myPanel.cancelPopup();
}
}
}
@@ -116,5 +116,6 @@ caches.indexerThreadsCount=-1
vcs.show.history.numbers=true
navbar.updateMergeTime=250
navbar.userActivityMergeTime=500
navbar.newpopup=false
inspectionGadgets.telemetry.enabled=false