User Interaction

Class GraphEditorInputMode

Class GraphEditorInputMode is a full-featured controller that makes available the functionality of a set of specialized input modes. It covers all aspects of user interaction with an IGraph instance displayed in a GraphCanvasComponent.

Figure 2.30. GraphEditorInputMode type hierarchy

GraphEditorInputMode type hierarchy.

General Features

GraphEditorInputMode provides convenient support for creating, modifying, and interacting with graph elements in an IGraph instance.

  • The IGraph instance that GraphEditorInputMode acts upon is accessible via the graph property.
  • The hitTestIterator property can be conveniently used for performing hit-tests.
  • Editing functionality can be easily customized for certain graph item types. Section "Customizing GraphEditorInputMode Interaction Functionality" describes the properties and callbacks provided by GraphEditorInputMode for this purpose.

GraphEditorInputMode also provides convenient support for hierarchically organized graphs, which includes, for example, grouping and ungrouping of nodes as well as re-parenting.

Upon initialization, GraphEditorInputMode creates and installs the input modes listed in Table 2.15, “Input modes used by GraphEditorInputMode (sorted by input mode priority)” as concurrent input modes. Note that the input modes are sorted according to their priority (see also the section called “Input Mode Priorities”).

Table 2.15. Input modes used by GraphEditorInputMode (sorted by input mode priority)

Type Name Description
HandleInputMode Manages the dragging of "handles," including the detection of beginning and ending of a drag, and canceling a drag. Also provides the visual feedback for handles when dragged.
KeyboardInputMode Recognizes key events.
ClickInputMode Recognizes mouse clicks.
MoveLabelInputMode Specialized instance that handles moving of labels to label candidate positions.
MoveInputMode Handles moving of canvas objects.
CreateBendInputMode Governs the creation of additional bends in edge paths.
CreateEdgeInputMode Governs the creation of edges in an IGraph.
MarqueeSelectionInputMode Provides the visual feedback for a rectangular selection box in the canvas.
ContextMenuInputMode Displays a context menu.
MouseHoverInputMode Displays a tooltip when the mouse rests in the canvas.
TextEditorInputMode Handles label editing.

Each of the input modes can be obtained or replaced using a like named property defined by GraphEditorInputMode. Also, a sorted list of input modes can be obtained via the sortedModes method.

Graph element creation functionality is provided directly by GraphEditorInputMode as well as the input modes CreateBendInputMode and CreateEdgeInputMode. The former uses the method shown in API Excerpt 2.23, “Mouse gesture node creation method” whenever a node is created in the canvas by means of a mouse click gesture. Using the nodeCreationAllowed and nodeCreator properties, the actual node creation behavior can be easily customized.

API Excerpt 2.23. Mouse gesture node creation method

createNode(clickPoint:IPoint):INode

Bend and edge creation is governed by their respective input modes. However, CreateEdgeInputMode exposes convenient bend-related customization capabilities. For example, the bendCreationAllowed property can be used to disallow bend creation in the canvas.

Customizing GraphEditorInputMode Interaction Functionality

Class GraphEditorInputMode provides various properties and callbacks that allow for fine-grained control of the editing functionality by restricting the set of graph items the input mode may act upon to a subset of the available graph item types. Most of these configuration properties can be set using a combination of the graph item type constants defined in class GraphItemTypes.

The available customization properties of class GraphEditorInputMode are listed in Table 2.16, “GraphEditorInputMode customization”.

Table 2.16. GraphEditorInputMode customization

Name Purpose
labelEditingAllowed Whether interactive label editing is allowed. If set to true, label editing is triggered by the F2 key by default.
nodeCreationAllowed Whether the user is allowed to create nodes.
selectableItems A combination of GraphItemTypes that specifies the set of graph items that can be selected by the user.
marqueeSelectableItems A combination of GraphItemTypes that specifies the set of graph items that can be selected using the marquee selection tool.
clickSelectableItems A combination of GraphItemTypes that specifies the set of graph items that can be selected by mouse clicks.
showHandleItems A combination of GraphItemTypes that specifies the set of graph items that should show resize handles when selected.
shouldShowHandles Callback that determines whether resize handles should be shown for the given item.
deletableItems A combination of GraphItemTypes that specifies the set of graph items that will be deleted when the Delete key is pressed.
shouldBeDeleted Callback that determines whether the given item may be deleted.
movableItems A combination of GraphItemTypes that specifies the set of graph items that can be moved by the user.
shouldBeMovable Callback that determines whether the given item may be moved.

Example 2.20, “Using the GraphItemTypes constants” shows how to use the GraphItemTypes constants to customize a GraphEditorInputMode instance.

Example 2.20. Using the GraphItemTypes constants

// don't show resize handles for any graph items
geim.showHandleItems = GraphItemTypes.NONE;

// only allow deletion of edges and nodes
geim.deletableItems = GraphItemTypes.EDGE | GraphItemTypes.NODE;

Class MainInputMode

Class MainInputMode defines a powerful basis for controller implementations that need to handle a broad range of user gestures happening in the view. It makes available the functionality of a set of specialized input modes that are installed concurrently with the canvas.

Figure 2.31. MainInputMode type hierarchy

MainInputMode type hierarchy.

The direct base type of class MainInputMode is MultiplexingInputMode, an input mode that enables input modes to be installed with the canvas and be executed concurrently. As part of the concurrency amongst these input modes, MultiplexingInputMode can grant an input mode exclusive control over all canvas interaction.

Input modes that support concurrency are of type IConcurrentInputMode and are also referred to as "concurrent input modes." They can be added to the set of input modes that are managed by MultiplexingInputMode by means of the methods listed in API Excerpt 2.24, “Methods for adding concurrent input modes”.

API Excerpt 2.24. Methods for adding concurrent input modes

// Adding concurrent input modes to a MultiplexingInputMode's concurrency 
// controller.
addConcurrent(inputMode:IConcurrentInputMode, priority:int):void

Input Mode Priorities

MultiplexingInputMode also introduces the notion of priority with the input modes it installs into the canvas. The priority value of an input mode determines when it is installed, which in turn defines its position in the queue of input modes that are delivered user events. A low priority value means that an input mode comes early in this queue and thus processes an event prior to other input modes with a higher priority value that are also registered with that event.

Upon initialization, MainInputMode creates and installs the input modes listed in Table 2.17, “Input modes used by MainInputMode (sorted by input mode priority)” as concurrent input modes. Note that the input modes are sorted according to their priority.

Table 2.17. Input modes used by MainInputMode (sorted by input mode priority)

Type Name Description
HandleInputMode Manages the dragging of "handles," including the detection of beginning and ending of a drag, and canceling a drag. Also provides the visual feedback for handles when dragged.
KeyboardInputMode Recognizes key events.
ClickInputMode Recognizes mouse clicks.
MoveInputMode Handles moving of canvas objects.
MarqueeSelectionInputMode Provides the visual feedback for a rectangular selection box in the canvas.
ContextMenuInputMode Displays a context menu.
MouseHoverInputMode Displays a tooltip when the mouse rests in the canvas.

Each of the input modes can be obtained and changed using a like named property defined by class MainInputMode. Also, a sorted list of input modes can be obtained by means of the sortedModes method.

Class MoveViewportInputMode

Class MoveViewportInputMode enables moving of the viewport by means of a simple mouse move gesture.

Customizing Input Modes

The behavior of input modes can be easily customized by means of their properties, using custom callback methods, or by replacing specialized input modes by customized implementations thereof.

For example, to customize the text of the tooltip that is displayed by MouseHoverInputMode, an appropriate callback method can be registered with the input mode's textProvider property. Example 2.21, “Setting text providers for the tooltips displayed by concurrent MouseHoverInputMode instances” presents this scheme in the context of two concurrent MouseHoverInputModes.

Example 2.21. Setting text providers for the tooltips displayed by concurrent MouseHoverInputMode instances

// 'mainInputMode' is of type com.yworks.canvas.input.MainInputMode.

var nodeTextProvider:Function = 
    TooltipTextProviders.getGraphItemMapperTextProvider(
        INode, "node-description");
var nodeHoverMode:MouseHoverInputMode = 
    new MouseHoverInputMode(new ToolTip(), nodeTextProvider);
nodeHoverMode.showEffect = fadeIn;
nodeHoverMode.hideEffect = fadeOut;

mainInputMode.addConcurrent(nodeHoverMode, 120);
			
var edgeTextProvider:Function = 
    TooltipTextProviders.getGraphItemMapperTextProvider(
        IEdge, "edge-description");
var edgeHoverMode:MouseHoverInputMode = 
    new MouseHoverInputMode(new ToolTip(), edgeTextProvider);
edgeHoverMode.showEffect = fadeIn;
edgeHoverMode.hideEffect = fadeOut;

mainInputMode.addConcurrent(edgeHoverMode, 120);

The context menu that is displayed by ContextMenuInputMode can be specified as a whole using the menu property.

Customizing User Interaction Behavior

Input modes that handle specific mouse gestures when a user interacts with the graph elements often either directly or indirectly use the look-up mechanism to query additional information in relation to the respective gesture. This information generally also affects the look and feel of the elements. Consequently, the look and feel of an application, i.e., the rendering and the behavior of graph elements whenever a user is interacting with them, can be conveniently customized by decorating their look-up. For instance, the selection decoration of nodes can be customized, or their resizing behavior can be changed to obey only discrete steps, simply by adding appropriate interface implementations to the look-up mechanism.

Example 2.22, “Decorating the look-up for nodes” shows how the look-up for nodes is decorated using the ILookupDecorator returned by the graph. A custom look-up chain link, which replaces the default look-up logic for nodes, is added to the look-up chain that is maintained by the graph. Whenever a node gets selected, the new logic then returns a custom ISelectionPaintable implementation.

More information on the look-up support provided by the graph structure implementation can be found in the section called “Look-up Support”.

Example 2.22. Decorating the look-up for nodes

// 'graph' is of type com.yworks.graph.model.IGraph.

// Decorate the look-up for nodes to change their default behavior.
var decorator:ILookupDecorator = 
  graph.lookup(ILookupDecorator) as ILookupDecorator;
if (decorator != null) {
  if (decorator.canDecorate(INode)) {
    decorator.addLookup(INode, 
      Lookups.addingLookupChainLink(ISelectionPaintable, 
                                    new MyCustomSelectionPaintable()));
  }
}

Table 2.18, “User gestures and interfaces implementations in the look-up” gives an overview on the interface implementations that are queried when certain user gestures are handled.

Table 2.18. User gestures and interfaces implementations in the look-up

Gesture Type Name Description
Edge creation IPortCandidateProvider Returns collections of ports that an edge can connect to. When a new edge is created, CreateEdgeInputMode queries the look-up of the source node and the target node for implementations of this interface.

Implementations of this interface include: EmptyPortsCandidateProvider, NodeCenterPortCandidateProvider, UnoccupiedPortCandidateProvider, PortCandidateProvider, and ShapeGeometryPortCandidateProvider.

Also, abstract class AbstractPortCandidateProvider is a convenience implementation of this interface.

Resizing IReshapeHandleProvider Offers control over the resize behavior for a model item that is displayed in the canvas. When a model item (INode) gets resized, HandleInputMode queries the item's look-up for implementations of this interface.

Class ReshapeableHandles is a convenience implementation of this interface.

ISizeConstraintProvider Enables definition of minimum and maximum size constraints for items of arbitrary type. In a hierarchically organized graph, the size of group nodes is affected by implementations of this interface.

Class SizeConstraintProvider is a convenient default implementation that allows to specify minimum and maximum size constraints for model items (INode). This type is in the look-up for non-leaf nodes in a hierarchically organized graph.

Selecting ISelectionPaintable Allows to install any number of ICanvasObject instances that make up the selection decoration for a model item that is displayed in the canvas. Whenever a model item gets selected, its look-up is queried by the SelectionPaintManager for implementations of this interface.

Implementations of this interface include: OrientedRectangleSelectionPaintable (by default in the look-up for ILabel instances), PointSelectionPaintable, RectangularHighlightPaintable, and RectangularSelectionPaintable (by default in the look-up for INode instances).

Edge reconnecting, port relocating IEdgePortCandidateProvider Returns collections of ports that an edge can connect to. When either end of an edge gets relocated, the look-up of the edge is queried for implementations of this interface.

Implementations of this interface include: DefaultEdgePortsCandidateProvider (by default in the look-up for IEdge instances), CurrentEdgePortsCandidateProvider, and AllCandidatesEdgePortCandidateProvider.

Moving IPositionHandler Provides callback methods that allow to control all parts of a mouse drag gesture. When a model item (INode, IPort, IBend, ILabel) gets moved, MoveInputMode and MoveLabelInputMode query the item's look-up for implementations of this interface.

Class DefaultPositionHandler is a convenient default implementation of this interface.

Customization of the re-parenting gesture in a hierarchically organized graph, which can be controlled using interface IReparentNodeHandler, is described in the section called “Class GraphEditorInputMode”.

Example 2.23, “Customizing the way a selected node is rendered” presents a custom implementation for interface ISelectionPaintable that draws a dotted rectangle around a selected node.

Example 2.23. Customizing the way a selected node is rendered

public class MyCustomSelectionPaintable extends ISelectionPaintable {
  override public function installSelectionPaintables(
    userObject:Object, canvas:CanvasComponent, 
    selectionGroup:ICanvasObjectGroup):Array {
    var n:INode = INode(userObject);
    var rect:ShapePaintable = 
      ShapePaintable.createViewRectangle(new DecoratedRectangle(n.layout, 3));
    rect.stroke = new Pen(Brushes.DarkGray);
    rect.stroke.weight = 2;
    
    return new ICanvasObject[]{ 
      canvas.add(rect, CanvasObjectDescriptor.Instance, selectionGroup) };
  }

  public class DecoratedRectangle extends IRectangle {
    private var box:IRectangle;
    private var borderWidth:Number;
    
    public DecoratedRectangle(box:IRectangle, borderWidth:Number) {
      this.box = box;
      this.borderWidth = borderWidth;
    }
    
    override public function get x():Number { 
      return box.x - borderWidth;
    }
    override public function get y():Number {
      return box.y - borderWidth;
    }
    
    override public function get width():Number {
      return box.width + 2 * borderWidth;
    }
    override public function get height():Number { 
      return box.height + 2 * borderWidth;
    }
  }
}