GraphSquare architecture III

In the previous part we looked at the MVC core, this time we’ll highlight the ideas and the code related to ‘tools’. A tool is basically a series of actions the user performs and which results in a command on the undo-stack. For example:

  • selecting some shapes with the typical rubberband is a tool, the series of actions being related to the mouse down, mouse move and mouse up events.
  • scaling or rotating a shape, which involves the adorners and the mouse events
  • creating a connection

and so on. The abstract tool concept originates from the fact that if you don’t package a series of actions in a separate class you inevitably have a complex logical flow in the overriden mouse-methods of the surface. So, instead of having various if-then-else statements in the MouseDown, MouseMove and MouseUp handlers you simply loop over all registered tools and whichever tool is ‘on’ can react accordingly. This approach has the additional advantage that you can

  • add tools with ease
  • disable or overrule other tools in the loop
  • chain tools, if necessary

Below is a snippet of the mouse handlers which are called by the surface corresponding to the mouse events on this surface. Note that the loop in these handlers are broken as soon as one of the tools have opted to handle the event.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public void MouseDown(MouseEventArgs e)
{
 
 
    foreach (ITool tool in toolList)
    {
        if (tool is Core.IMouseListener)
            if ((tool as Core.IMouseListener).MouseDown(e))
                return;
    }
 
}
public void MouseMove(MouseEventArgs e)
{
    foreach (ITool tool in toolList)
    {
        if (tool is Core.IMouseListener)
            if ((tool as Core.IMouseListener).MouseMove(e))
                return;
    }
 
}
public void MouseUp(MouseEventArgs e)
{
    foreach (ITool tool in toolList)
    {
        if (tool is Core.IMouseListener)
            if ((tool as Core.IMouseListener).MouseUp(e))
                return;
    }
 
}

Most of the tools have some kind of visual feedback, on creating a rectangle you would see for example a rubberband which previews the end-result. For this purpose a few classes are necessray which specializes the ITool interface (depicted above) and all tools which need a rubberband are inheriting from it. The whole rubberband (sometimes called ‘marching ants’) affair is also taken separately in other constructs (a static helper class) and the process of creating tools becomes in the end a game of putting some blocks together. In fact, a dummy tool with visual feedback would look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class DummyTool : RubberCreationToolBase
{
 
 
    #region Constructor
    ///<summary>
    ///Default constructor
    ///</summary>
    public DummyTool():base("Dummy Tool")
    {
 
    }
    #endregion
    public override void OnExecute(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
    {
        GhostManager.Surface = sender as DiagramSurface;
        base.OnExecute(sender, e);
    }
 
    public override void DrawGhost(System.Windows.Rect rec)
    {
        GhostManager.DrawRectangularGhost(rec);
    }
 
    public override void OnRubberCreated(System.Windows.Rect rec)
    {
        Surface.Controller.Output("The '" +this.Name + "' tool finished but no realshape was added.");
    }
}

The overridable DrawGhost method allows you to give whatever feedback you wish while the overridable OnRubberCreated allows you to do whatever you wish after the rubber was creates, i.e. after the user released the mouse. It’s precisely in this method that one should package the action (say, the creation of a shape) in a command and push it onto the undo-stack. In the dummy tool above only an informative message is displayed.

Of course, in order to manage the available tools and to connect them to the UI you need some more code in the form of some tool manager. The tool manager is just a wrapped generic list created through Unity inside the surface class. Note, in this context, that the concept of a tool only makes sense on the UI level and is, hence, not re-usable in other surfaces (e.g. a web-interface).

In the next part we’ll discuss the other end of the architecture; data abstraction and the serialization of diagrams.

Related Posts

A functional graph library in F#

Read more

Twirl: like Live Pivot but better

We re-invented Live Pivot and took away the limitations along the way.

Read more

2 Responses to GraphSquare architecture III

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

top