Bug 252987 - keyboard navigation trough graph
Summary: keyboard navigation trough graph
Status: NEW
Alias: None
Product: GEF
Classification: Tools
Component: GEF-Legacy Zest (show other bugs)
Version: unspecified   Edit
Hardware: All All
: P3 enhancement (vote)
Target Milestone: ---   Edit
Assignee: gef-inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-10-31 11:30 EDT by Eugene Kuleshov CLA
Modified: 2009-10-08 10:54 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
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);
	}