import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.ui.content.Content;
+import com.intellij.ui.switcher.SwitchTarget;
import com.intellij.ui.tabs.JBTabs;
import com.intellij.ui.tabs.TabInfo;
import com.intellij.ui.tabs.TabsListener;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Set;
public class GridCellImpl implements GridCell, Disposable {
return myMinimizedContents.contains(content);
}
+ public java.util.List<SwitchTarget> getTargets(boolean onlyVisible) {
+ return myTabs.getTargets(onlyVisible);
+ }
+
+ public SwitchTarget getTargetForSelection() {
+ return myTabs.getCurrentTarget();
+ }
+
+ public boolean contains(Component c) {
+ return myTabs.getComponent().isAncestorOf(c);
+ }
+
private static class ProviderWrapper extends NonOpaquePanel implements DataProvider {
Content myContent;
import com.intellij.openapi.util.Disposer;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.ui.content.Content;
+import com.intellij.ui.switcher.SwitchProvider;
+import com.intellij.ui.switcher.SwitchTarget;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
return getCellFor(content).isMinimized(content);
}
+
static class Placeholder extends Wrapper implements NullableComponent {
private JComponent myContent;
public String getSessionName() {
return mySessionName;
}
+ public SwitchTarget getCellFor(Component c) {
+ Component eachParent = c;
+ while (eachParent != null) {
+ for (GridCellImpl eachCell : myContent2Cell.values()) {
+ if (eachCell.contains(eachParent)) {
+ return eachCell.getTargetForSelection();
+ }
+ }
+
+ eachParent = eachParent.getParent();
+ }
+
+ return null;
+ }
+
+
+ public List<SwitchTarget> getTargets(boolean onlyVisible) {
+ Collection<GridCellImpl> cells = myPlaceInGrid2Cell.values();
+ ArrayList<SwitchTarget> result = new ArrayList<SwitchTarget>();
+ for (GridCellImpl each : cells) {
+ if (!each.isDetached()) {
+ result.addAll(each.getTargets(onlyVisible));
+ }
+ }
+ return result;
+ }
}
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.ui.awt.RelativeRectangle;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.components.panels.Wrapper;
import com.intellij.ui.content.*;
+import com.intellij.ui.switcher.SwitchProvider;
+import com.intellij.ui.switcher.SwitchTarget;
import com.intellij.ui.tabs.JBTabs;
import com.intellij.ui.tabs.TabInfo;
import com.intellij.ui.tabs.TabsListener;
import java.util.*;
import java.util.List;
-public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Facade, ViewContextEx, PropertyChangeListener {
+public class RunnerContentUi implements ContentUI, Disposable, CellTransform.Facade, ViewContextEx, PropertyChangeListener, SwitchProvider {
@NonNls public static final String LAYOUT = "Runner.Layout";
@NonNls public static final String VIEW_POPUP = "Runner.View.Popup";
return myRunnerUi;
}
+ public SwitchTarget getCurrentTarget() {
+ Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
+ if (owner == null) return myTabs.getCurrentTarget();
+ GridImpl grid = getSelectedGrid();
+ if (grid.getContents().size() <= 1) return myTabs.getCurrentTarget();
+
+ SwitchTarget cell = grid.getCellFor(owner);
+
+ return cell != null ? cell : myTabs.getCurrentTarget();
+ }
+
+ public List<SwitchTarget> getTargets(boolean onlyVisible) {
+ List<SwitchTarget> result = new ArrayList<SwitchTarget>();
+
+ result.addAll(myTabs.getTargets(true));
+ result.addAll(getSelectedGrid().getTargets(onlyVisible));
+
+ return result;
+ }
+
+ private class LayoutTarget implements SwitchTarget {
+ public ActionCallback switchTo(boolean requestFocus) {
+ return null;
+ }
+
+ public boolean isVisible() {
+ return false;
+ }
+
+ public RelativeRectangle getRectangle() {
+ return null;
+ }
+ }
}
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComponentWithActions;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.ui.content.ContentFactory;
import com.intellij.ui.content.ContentManager;
import com.intellij.ui.content.ContentManagerListener;
+import com.intellij.ui.switcher.SwitchProvider;
+import com.intellij.ui.switcher.SwitchTarget;
+import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
myContentUI = new RunnerContentUi(project, this, ActionManager.getInstance(), IdeFocusManager.getInstance(project), myLayout,
runnerTitle + " - " + sessionName);
- myContentPanel = new JPanel(new BorderLayout());
+ myContentPanel = new MyContent();
myViewsContentManager = getContentFactory().createContentManager(myContentUI.getContentUI(), false, project);
Disposer.register(this, myViewsContentManager);
}
return contents;
}
+
+ private class MyContent extends JPanel implements DataProvider {
+ public MyContent() {
+ super(new BorderLayout());
+ }
+
+ public Object getData(@NonNls String dataId) {
+ if (SwitchProvider.KEY.getName().equals(dataId)) {
+ return myContentUI;
+ }
+
+ return null;
+ }
+ }
+
}
}
private SwitchProvider getProvider(AnActionEvent e) {
- return new SwitchProvider() {
- public SwitchTarget[] getTargets() {
- return new SwitchTarget[0];
- }
- };
- //return e.getData(SwitchProvider.KEY);
+ return e.getData(SwitchProvider.KEY);
}
protected abstract void move(SwitchingSession session);
import com.intellij.openapi.actionSystem.DataKey;
+import javax.swing.*;
+import java.util.List;
+
public interface SwitchProvider {
DataKey<SwitchProvider> KEY = DataKey.create("SwitchProvider");
- SwitchTarget[] getTargets();
+ List<SwitchTarget> getTargets(boolean onlyVisible);
+ SwitchTarget getCurrentTarget();
+
+ JComponent getComponent();
}
*/
package com.intellij.ui.switcher;
-public class SwitchTarget {
+import com.intellij.openapi.util.ActionCallback;
+import com.intellij.ui.awt.RelativeRectangle;
+
+public interface SwitchTarget {
+
+ ActionCallback switchTo(boolean requestFocus);
+
+ boolean isVisible();
+ RelativeRectangle getRectangle();
+
}
*/
package com.intellij.ui.switcher;
+import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.ui.AbstractPainter;
+import com.intellij.openapi.ui.Painter;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.wm.IdeGlassPane;
+import com.intellij.openapi.wm.IdeGlassPaneUtil;
+import com.intellij.openapi.wm.impl.content.GraphicsConfig;
+import javax.swing.*;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
+import java.util.*;
+import java.util.List;
-public class SwitchingSession implements KeyEventDispatcher {
+import static java.lang.Math.abs;
+import static java.lang.Math.sqrt;
+
+public class SwitchingSession implements KeyEventDispatcher, Disposable {
private SwitchProvider myProvider;
private AnActionEvent myInitialEvent;
private boolean myFinished;
+ private java.util.List<SwitchTarget> myTargets;
+ private IdeGlassPane myGlassPane;
+
+ private Map<SwitchTarget, TargetPainer> myPainters = new Hashtable<SwitchTarget, TargetPainer>();
+ private JComponent myRootComponent;
+ private SwitchTarget mySelection;
public SwitchingSession(SwitchProvider provider, AnActionEvent e) {
myProvider = provider;
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
+
+ myTargets = myProvider.getTargets(true);
+ if (myTargets.size() == 0) {
+ Disposer.dispose(this);
+ return;
+ }
+
+
+ mySelection = myProvider.getCurrentTarget();
+
+ myGlassPane = IdeGlassPaneUtil.find(myProvider.getComponent());
+ for (SwitchTarget each : myTargets) {
+ TargetPainer eachPainter = new TargetPainer(each);
+ Disposer.register(this, eachPainter);
+
+ myRootComponent = myProvider.getComponent();
+ myGlassPane.addPainter(myRootComponent, eachPainter, this);
+ myPainters.put(each, eachPainter);
+ }
+
}
public boolean dispatchKeyEvent(KeyEvent e) {
return false;
}
+ private SwitchTarget getSelection() {
+ return mySelection;
+ }
+
+ private class TargetPainer extends AbstractPainter implements Disposable {
+
+ private SwitchTarget myTarget;
+
+ private TargetPainer(SwitchTarget target) {
+ myTarget = target;
+ }
+
+ @Override
+ public void executePaint(Component component, Graphics2D g) {
+ GraphicsConfig cfg = new GraphicsConfig(g);
+ cfg.setAntialiasing(true);
+
+ g.setColor(Color.red);
+ Rectangle paintRect = myTarget.getRectangle().getRectangleOn(component);
+
+ boolean selected = myTarget.equals(getSelection());
+ if (selected) {
+ g.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, new float[] {2, 4}, 0));
+ g.draw(paintRect);
+ } else {
+ g.setStroke(new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, new float[] {2, 4}, 0));
+ g.draw(paintRect);
+ }
+
+ cfg.restore();
+ }
+
+ @Override
+ public boolean needsRepaint() {
+ return true;
+ }
+
+ public void dispose() {
+ myGlassPane.removePainter(this);
+ }
+ }
+
+ private enum Direction {
+ up, down, left, right
+ }
public void up() {
- System.out.println("SwitchingSession.up");
+ setSelection(getNextTarget(Direction.up));
}
public void down() {
- System.out.println("SwitchingSession.down");
+ setSelection(getNextTarget(Direction.down));
}
public void left() {
- System.out.println("SwitchingSession.left");
+ setSelection(getNextTarget(Direction.left));
}
public void right() {
- System.out.println("SwitchingSession.right");
+ setSelection(getNextTarget(Direction.right));
}
- private void finish() {
+ private void setSelection(SwitchTarget target) {
+ mySelection = target;
+
+ for (TargetPainer each : myPainters.values()) {
+ each.setNeedsRepaint(true);
+ }
+ }
+
+ private SwitchTarget getNextTarget(Direction direction) {
+ List<Point> points = new ArrayList<Point>();
+ Point selected = null;
+ Map<SwitchTarget, Point> target2Point = new HashMap<SwitchTarget, Point>();
+ for (SwitchTarget each : myTargets) {
+ Rectangle eachRec = each.getRectangle().getRectangleOn(myRootComponent);
+ Point eachPoint = null;
+ switch (direction) {
+ case up:
+ eachPoint = new Point(eachRec.x + eachRec.width / 2, eachRec.y + eachRec.height);
+ break;
+ case down:
+ eachPoint = new Point(eachRec.x + eachRec.width, eachRec.y);
+ break;
+ case left:
+ eachPoint = new Point(eachRec.x + eachRec.width, eachRec.y + eachRec.height / 2);
+ break;
+ case right:
+ eachPoint = new Point(eachRec.x, eachRec.y + eachRec.height / 2);
+ break;
+ }
+
+ if (each.equals(mySelection)) {
+ switch (direction) {
+ case up:
+ selected = new Point(eachRec.x + eachRec.width / 2, eachRec.y);
+ break;
+ case down:
+ selected = new Point(eachRec.x + eachRec.width / 2, eachRec.y + eachRec.height);
+ break;
+ case left:
+ selected = new Point(eachRec.x, eachRec.y + eachRec.height / 2);
+ break;
+ case right:
+ selected = new Point(eachRec.x + eachRec.width, eachRec.y + eachRec.y / 2);
+ break;
+ }
+ }
+ points.add(eachPoint);
+ target2Point.put(each, eachPoint);
+ }
+
+ TreeMap<Integer, SwitchTarget> distance = new TreeMap<Integer, SwitchTarget>();
+ for (SwitchTarget eachTarget : myTargets) {
+ Point eachPoint = target2Point.get(eachTarget);
+ if (selected == eachPoint) continue;
+
+ double eachDistance = sqrt(abs(eachPoint.getX() - selected.getX())) + sqrt(abs(eachPoint.getY() - selected.getY()));
+ distance.put((int)eachDistance, eachTarget);
+ }
+
+
+ for (Integer eachDistance : distance.keySet()) {
+ SwitchTarget eachTarget = distance.get(eachDistance);
+ Point eachPoint = target2Point.get(eachTarget);
+ switch (direction) {
+ case up:
+ if (eachPoint.y < selected.y) {
+ return eachTarget;
+ }
+ break;
+ case down:
+ if (eachPoint.y > selected.y) {
+ return eachTarget;
+ }
+ break;
+ case left:
+ if (eachPoint.x < selected.x) {
+ return eachTarget;
+ }
+ break;
+ case right:
+ if (eachPoint.x > selected.x) {
+ return eachTarget;
+ }
+ break;
+ }
+ }
+
+ return distance.values().iterator().next();
+ }
+
+ private void selectNext() {
+ int index = myTargets.indexOf(getSelection());
+ if (index + 1 < myTargets.size()) {
+ mySelection = myTargets.get(index + 1);
+ } else {
+ mySelection = myTargets.get(0);
+ }
+
+ for (TargetPainer each : myPainters.values()) {
+ each.setNeedsRepaint(true);
+ }
+ }
+
+ public void dispose() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this);
myFinished = true;
+ }
- System.out.println("SwitchingSession.finish");
+ private void finish() {
+ SwitchTarget selection = getSelection();
+ if (selection != null) {
+ selection.switchTo(true);
+ }
+ Disposer.dispose(this);
}
public boolean isFinished() {
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Getter;
+import com.intellij.ui.switcher.SwitchProvider;
+import com.intellij.ui.switcher.SwitchTarget;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Comparator;
-public interface JBTabs {
+public interface JBTabs extends SwitchProvider {
@NotNull
TabInfo addTab(TabInfo info, int index);
import com.intellij.openapi.wm.IdeGlassPaneUtil;
import com.intellij.openapi.wm.impl.content.GraphicsConfig;
import com.intellij.ui.CaptionPanel;
+import com.intellij.ui.awt.RelativeRectangle;
+import com.intellij.ui.switcher.SwitchTarget;
import com.intellij.ui.tabs.*;
import com.intellij.ui.tabs.impl.singleRow.SingleRowLayout;
import com.intellij.ui.tabs.impl.singleRow.SingleRowPassInfo;
import com.intellij.util.ui.Animator;
import com.intellij.util.ui.TimedDeadzone;
import com.intellij.util.ui.UIUtil;
+import com.intellij.util.ui.update.ComparableObject;
import com.intellij.util.ui.update.LazyUiDisposable;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
revalidate();
repaint();
}
+
+ public List<SwitchTarget> getTargets(boolean onlyVisible) {
+ ArrayList<SwitchTarget> result = new ArrayList<SwitchTarget>();
+ for (TabInfo each : myVisibleInfos) {
+ result.add(new TabTarget(each));
+ }
+ return result;
+ }
+
+ public SwitchTarget getCurrentTarget() {
+ return new TabTarget(getSelectedInfo());
+ }
+
+ private class TabTarget extends ComparableObject.Impl implements SwitchTarget {
+
+ private TabInfo myInfo;
+
+ private TabTarget(TabInfo info) {
+ myInfo = info;
+ }
+
+ public ActionCallback switchTo(boolean requestFocus) {
+ return select(myInfo, requestFocus);
+ }
+
+ public boolean isVisible() {
+ return getRectangle() != null;
+ }
+
+ public RelativeRectangle getRectangle() {
+ TabLabel label = myInfo2Label.get(myInfo);
+ if (label.getRootPane() == null) return null;
+
+ Rectangle b = label.getBounds();
+ b.x += 2;
+ b.width -= 4;
+ b.y += 2;
+ b.height -= 4;
+ return new RelativeRectangle(label.getParent(), b);
+ }
+
+ @Override
+ public Object[] getEqualityObjects() {
+ return new Object[] {myInfo};
+ }
+ }
}