Bug 252987

Summary: keyboard navigation trough graph
Product: [Tools] GEF Reporter: Eugene Kuleshov <ekuleshov>
Component: GEF-Legacy ZestAssignee: gef-inbox <gef-inbox>
Status: NEW --- QA Contact:
Severity: enhancement    
Priority: P3 CC: eclipse, irbull
Version: unspecified   
Target Milestone: ---   
Hardware: All   
OS: All   
Whiteboard:

Description Eugene Kuleshov CLA 2008-10-31 11:30:39 EDT
We are using Zest to show dependency graph in m2e and users asking to provide keyboard navigation trough the graph. I think it would make sense to implement such feature at the framework level. Thanks.
Comment 1 Ian Bull CLA 2008-10-31 11:32:56 EDT
I agree.  Do you have particular ideas? Arrow keys that navigate to the closest node in a particular direction?  
Comment 2 Eugene Kuleshov CLA 2008-10-31 11:42:23 EDT
(In reply to comment #1)
> Arrow keys that navigate to the closest node in a particular direction?

That make sense. Also, when graph is on the scrollable panel and is bigger then visible area it should scroll to selected element if it get behind visble area. 

In addition, user should be able to open popup menu using standard keyboard shortcuts (like Ctrl-F10) and perhaps be able to see a tooltip (like F2 in other Eclipse editors), but those should be easy to handle in the user's code, so I am not much worried about them.
Comment 3 Christian Sowada CLA 2009-10-08 10:54:54 EDT
I've had same problem and build a small solution. Here for all intrested persons.


Extended GraphViewer:

	private void selectNearbyNode(int direction) {

		GraphNode node = (GraphNode)graph.getSelection().get(0);

		org.eclipse.draw2d.geometry.Point srcPt = node.getLocation();

		List<GraphNode> nodes = graph.getNodes();

		GraphNode bestMatch = null;
		int bestAlignmentDifference = 0;
		double bestDirectDifference = 0;

		for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
			GraphNode object = (GraphNode) iterator.next();

			org.eclipse.draw2d.geometry.Point tgtPt = object.getLocation();

			// Winkel: Links 0°, oben 90°, unten 270° rechts 180°
			float angle = (float)Math.atan2(tgtPt.y - srcPt.y, tgtPt.x - srcPt.x) 
			* 180 / (float)Math.PI;
			
			// Winkel korrgieren, damit er von 0 -360° geht, nicht von 0 - -/+ 180°
			angle += 180;

			boolean useVertical = false;
			int min = 0;
			int max = 0;

			switch (direction) {
			case SWT.TOP:
				min = 45;
				max = 135;
				break;

			case SWT.BOTTOM:
				min = 225;
				max = 315;
				break;

			case SWT.LEFT:
				min = 315;
				max = 405;

				// Wegen dem 0 Übergang etwas nachhelfen
				if(angle > 0 && angle < 45)
					angle += 360;
				useVertical = true;
				break;

			case SWT.RIGHT:
				min = 135;
				max = 225;
				useVertical = true;
				break;	

			default:
				break;
			}

			if(object != node) {
				if(angle > min && angle < max) {

					int diff = useVertical ? tgtPt.x - srcPt.x : tgtPt.y - srcPt.y;
					int otherDiff = useVertical ? tgtPt.y - srcPt.y : tgtPt.x - srcPt.x;
					
					if(diff < 0)
						diff *= -1;
					
					if(otherDiff < 0)
						otherDiff *= -1;

					if(bestMatch == null) {
						bestMatch = object;
						bestAlignmentDifference = diff;
						bestDirectDifference = Math.sqrt(Math.pow(diff, 2) + 
								Math.pow(otherDiff, 2));
					} else if(bestAlignmentDifference > diff) {
						bestMatch = object;
						bestAlignmentDifference = diff;
						bestDirectDifference = Math.sqrt(Math.pow(diff, 2) + 
								Math.pow(otherDiff, 2));
					} else if(bestAlignmentDifference == diff) {
						double diffx = Math.sqrt(Math.pow(diff, 2) + 
								Math.pow(otherDiff, 2));
						
						if(diffx < bestDirectDifference) {
							bestMatch = object;
							bestAlignmentDifference = diff;
							bestDirectDifference = Math.sqrt(Math.pow(diff, 2) + 
									Math.pow(otherDiff, 2));
						}
					}
				}
			}
		}

		if(bestMatch != null)
			graph.setSelection(new GraphItem[] {bestMatch});
	}



Registered Keylistener:

	public void keyReleased(KeyEvent e) {
		if(e.keyCode == SWT.ARROW_UP)
			selectNearbyNode(SWT.TOP);

		if(e.keyCode == SWT.ARROW_DOWN)
			selectNearbyNode(SWT.BOTTOM);

		if(e.keyCode == SWT.ARROW_LEFT)
			selectNearbyNode(SWT.LEFT);

		if(e.keyCode == SWT.ARROW_RIGHT)
			selectNearbyNode(SWT.RIGHT);
	}