Trigger Rotation and/or Zooming via Keyboard
Tips & TricksSummary
Using key events to trigger view and graph interaction.
Description
Using standard Java features it is possible to use key events to interact with yFiles' viewer component in many ways.
The following sample code demonstrates how to rotate a graph when pressing SHIFT+CURSOR_LEFT or SHIFT+CURSOR_RIGHT and zooming in and out when pressing SHIFT+CURSOR_UP or SHIFT+CURSOR_DOWN.
package demo.view;
import y.base.NodeCursor;
import y.base.EdgeCursor;
import y.base.YCursor;
import y.base.YList;
import y.geom.YPoint;
import y.layout.BufferedLayouter;
import y.layout.GraphLayout;
import y.layout.Layouter;
import y.layout.LayoutGraph;
import y.view.Graph2D;
import y.view.Graph2DLayoutExecutor;
import y.view.LayoutMorpher;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.Action;
import javax.swing.Timer;
/**
* Demonstrates how to map key events to view animations.
*
* Shift + cursor left/cursor right will rotate the current graph counter-clockwise/
* clockwise around the center of the view.
* Shift + cursor up/cursor down will zoom in/out.
*
* Note:
* Key events are inherently platform (and even keyboard) dependent.
* On linux, holding down a non-modifier key will by default generate keyPressed
* followed by keyReleased after an initial delay and then
* keyPressed/keyReleased event pairs in rapid succession as long as the key is
* held down. This behavior can be disabled though.
* On windows, multiple keyPressed events are generated but only one keyReleased
* event.
*/
public class RotateDemo extends DemoBase {
/**
* Registers the rotate action and the zoom action and maps them to the
* approriate key events.
*/
protected void registerViewActions() {
// keep the default action
super.registerViewActions();
// NOTE: trapping key events has to be done on the view's canvas!!!
final JComponent canvas = view.getCanvasComponent();
// register rotation
canvas.addKeyListener(
new ActionTrigger(new RotateAction(false), KeyEvent.VK_LEFT));
canvas.addKeyListener(
new ActionTrigger(new RotateAction(true), KeyEvent.VK_RIGHT));
// register zooming
canvas.addKeyListener(
new ActionTrigger(new ZoomAction(true), KeyEvent.VK_UP));
canvas.addKeyListener(
new ActionTrigger(new ZoomAction(false), KeyEvent.VK_DOWN));
}
/**
* Rotates the graph in the view be 10 degress clockwise/counter-clockwise.
*/
final class RotateAction extends AbstractAction {
boolean clockwise;
RotateAction( final boolean clockwise ) {
this.clockwise = clockwise;
}
public void actionPerformed( final ActionEvent e ) {
//Note: for all versions before yFiles for Java 2.7 you have to use the
//second version of this method which is stated at the end of the article.
// calculate the geometric info of the rotated graph
final RotateBy10Degrees rotator = new RotateBy10Degrees(clockwise);
Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor(Graph2DLayoutExecutor.ANIMATED);
// apply the geometric info in an animated fashion
LayoutMorpher morpher = executor.getLayoutMorpher();
morpher.setPreferredDuration(25);
morpher.setKeepZoomFactor(true);
executor.doLayout(view, rotator);
}
/**
* Calculates a layout that is rotated 10 degrees clockwise/
* counter-clockwise around the center of the view.
*/
final class RotateBy10Degrees implements Layouter {
private final double radians;
RotateBy10Degrees( final boolean clockwise ) {
this.radians = clockwise ? Math.PI/18.0 : -Math.PI/18.0;
}
public boolean canLayout( final LayoutGraph graph ) {
return true;
}
public void doLayout( final LayoutGraph graph ) {
if (graph == null || graph.isEmpty()) {
return;
}
// set up rotation: 10 degrees around the center of the view
final Point2D center = view.getCenter();
final AffineTransform at = new AffineTransform();
at.rotate(radians, center.getX(), center.getY());
final Point2D coords = new Point2D.Double();
// rotate node centers
for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
coords.setLocation(
graph.getCenterX(nc.node()), graph.getCenterY(nc.node()));
at.transform(coords, coords);
graph.setCenter(nc.node(), coords.getX(), coords.getY());
}
// rotate edges
for (EdgeCursor ec = graph.edges(); ec.ok(); ec.next()) {
final YList newPath = new YList();
final YList path = graph.getPointList(ec.edge());
for (YCursor pc = path.cursor(); pc.ok(); pc.next()) {
final YPoint yp = (YPoint)pc.current();
coords.setLocation(yp.getX(), yp.getY());
at.transform(coords, coords);
newPath.add(new YPoint(coords.getX(), coords.getY()));
}
graph.setPoints(ec.edge(), newPath);
}
}
}
}
/**
* Multiplies/divides the current zoom level of the view by 1.25.
*/
final class ZoomAction extends AbstractAction {
private final double factor;
ZoomAction( final boolean zoomIn ) {
this.factor = zoomIn ? 1.25 : 1.0/1.25;
}
public void actionPerformed( final ActionEvent e ) {
view.setZoom(view.getZoom()*factor);
view.updateView();
}
}
private static final class ActionTrigger extends KeyAdapter {
private static final int LATENCY = 35;
private final int keyCode;
private final Timer timer;
ActionTrigger( final Action action, final int keyCode ) {
this.keyCode = keyCode;
this.timer = new Timer(LATENCY, action);
this.timer.setInitialDelay(0);
this.timer.setRepeats(true);
}
public void keyPressed( final KeyEvent e ) {
if (keyCode == e.getKeyCode() &&
KeyEvent.SHIFT_DOWN_MASK == e.getModifiersEx()) {
if (!timer.isRunning()) {
timer.restart();
}
}
}
public void keyReleased( final KeyEvent e ) {
if (keyCode == e.getKeyCode()) {
timer.stop();
}
}
}
/**
* Instantiates and starts this demo.
*/
public static void main( String[] args ) {
EventQueue.invokeLater(new Runnable() {
public void run() {
initLnF();
(new RotateDemo()).start();
}
});
}
}
public void actionPerformed( final ActionEvent e ) {
// the graph to rotate
Graph2D graph = view.getGraph2D();
// calculate the geometric info of the rotated graph
final RotateBy10Degrees rotator = new RotateBy10Degrees(clockwise);
GraphLayout layout = (new BufferedLayouter(rotator)).calcLayout(graph);
// apply the geometric info in an animated fashion
LayoutMorpher morpher = new LayoutMorpher(view, layout);
morpher.setPreferredDuration(25);
morpher.setKeepZoomFactor(true);
morpher.execute();
}
Resources
Categories this article belongs to:
yFiles for Java > yFiles Viewer > Displaying and Editing Graphs > User Interaction
Applies to:
yFiles for Java 2: 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 2.10, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18
Keywords:
Graph2DView - key event - rotate - rotation - zoom - zooming