Adding Marker Mouse Messages
In order to develop interactive charts we need some mouse events on line markers.
Users may want to display detailed data when the mouse is over a marker or show an editor when the user clicks on it.
The final modification we will make is to add add mouse events to the markers.
Detecting the mouse marker
The first thing we need to do is detect which marker the mouse is in the general vicinity of.
We will use a fudge factor of 5 to create a rectangle around the mouse position and use that to determine if the calculated marker positions were relatively close to the mouse.
We use an list instance variable mMouseMarker to store the last known mouse marker X,Y.
The X,Y here is x label, y value for example “wed,889″.
Add instance variables
The first step is to add two list instance variables.
- mMouseMarker
- mMouseDownMarker
These list variables are used to store the last known mouse marker and mouse down marker, allowing us to determine if the mouse marker has changed.
Add the variable definitions to the LCB source after the current instance variable declarations.
widget community.livecode.elanorb.modifiedlinegraph    … module imports   … widget meta data   … property definitons   … current instance variables   // Mouse Events  private variable mMouseMarker as List  private variable mMouseDownMarker as List   … code continues end widget
The OnMouseMove handler
The first message we will handle is OnMouseMove.
The  OnMouseMove message is sent when the mouse pointer moves within the widget’s rect.
When the message is received we work out whether the mouse has entered or left a marker and post the relevant message to the widget object.
- Store the current mouse marker
- Update the current mouse marker
- Check if the new mouse marker differs from the stored(previous) mouse marker. If the mouse marker has changed
- If the stored mouse marker is not empty(there was a previous marker) the mouse has left the stored (previous) marker so send a markerMouseLeave message to the widget object with the stored (previous) marker as a parameter.
- If the new mouse marker is not empty(a marker was found close to the mouse position) the mouse has entered the area of a new marker so send a markerMouseEnter message to the widget object with the new marker as a parameter.
- If the mouse has not entered or left a marker no message is sent.
Note: If you need a refresher on handling mouse events have a look back at Lesson 4: Step 4 – Mouse Events.
The OnMouseMove handler
Add the OnMouseMove handler.
- Declare the handler.
- public: used externally
- returns nothing: has no return value
- Store the current mouse marker.
- Get the new mouse marker.
- Check the new marker is not the previous marker.
- If there was a previous marker  the mouse has left that marker so post markerMouseLeave to the widget object with the previous marker as a parameter.
- If there is a new marker  the mouse has entered that marker so post markerMouseEnter to the widget object with the previous marker as a parameter
public handler OnMouseMove() returns nothing    variable tMouseMarker as List    put mMouseMarker into tMouseMarker   UpdateMouseMarker()   if mMouseMarker is not tMouseMarker then       if tMouseMarker is not [] then           post "markerMouseLeave" with tMouseMarker       end if       if mMouseMarker is not [] then           post "markerMouseEnter" with mMouseMarker       end if   end if end handler
The UpdateMouseMarker handler
The UpdateMouseMarker handler does most of the work.
- Checks if the mouse has been clicked with the grid area
- Iterates across each line on the graph
- Iterates across each value in the list of values
- Calculates the x,y position of the corresponding point on the graph
- Works out the rect around the point using the fudge factor of 5 in each direction.
- Works out if the mouse location is within that rectangle
- Updates mMouseMarker
- Iterates across each value in the list of values
- When the handler finished mMouseMarker will either contain
- A reference to a marker, if one was found close enough to the click location
- Nothing, if there was no marker close enough to the click location.
Add a constant definition kHitTestFudgeFactor. We add the constant here as only UpdateMouseMarker uses it. If you prefer you can add this definition with the other module level declarations.
Add the UpdateMouseMarker handler.
- Declare the private handler with no return value.
- Declare variables
- tMouseMarker: a list to store the marker, or empty if no maker is found
- tMouseLoc: a point to store the mouse location.
- Update tMouseLoc with the mouse position.
- Check the mouse location is within the grid area.
- Iterate across each graph line (mData is a list variable, element 1 is the X axis labels, element 2 is the values of line 1 etc)
- Iterate across each value (element) in the list of values for the current line.
constant kHitTestFudgeFactor is 5 private handler UpdateMouseMarker() returns nothing variable tMouseMarker as List variable tMouseLoc as Point put the mouse position into tMouseLoc if tMouseLoc is within mGridRect then    variable tGraphLine as Integer    repeat with tGraphLine from 2 up to the number of elements in mData        variable tDataPoints as List        put element tGraphLine of mData into tDataPoints        variable tPointIndex as Integer        repeat with tPointIndex from 1 up to the number of elements in tDataPoints        … code continues        end repeat    end repeat end if end handler
- Check the current value is not nothing.
- Define variables tPointX and tPointY.
- Calculate the X coordinate of the current value, tPointX .
- The left of the grid + (the index of the current point -1) * column width.
- Calculate the Y coordinate of the current value, tPointY.
- Define variable tHeightRatio
- Calculate tHeightRatio as the percentage of the  grid height the current value takes up.
- tPointY = the bottom of the grid – (the grid height * tHeightRatio).
private handler UpdateMouseMarker() returns nothing    … previous code   repeat with tPointIndex from 1 up to the number of elements in tDataPoints      if tDataPoints[tPointIndex] is not nothing then          variable tPointX as Real          variable tPointY as Real          put the left of mGridRect + ((tPointIndex - 1) * mGridHWidth) into tPointX          variable tHeightRatio as Real          put (tDataPoints[tPointIndex] - mYMin) / (mYMax - mYMin) into tHeightRatio         put the bottom of mGridRect - the height of mGridRect * tHeightRatio into tPointY   …. code continues      end if   end repeat   … previous code end handler
- Define rectangle variable tRect.
- Calculate the hit area around the point and store it in tRect. The rectangle points are kHitTestFudgeFactor pixels away from the point in each direction.
- Check if tMouseLoc is within tRect
- If it is update tMouseMarker with
- X = the X axis label of the point
- Y = the value of the point
- If it is update tMouseMarker with
- Update mMouseMarker with the value of tMouseMarker.
The instance variable mMouseMarker now contains a references to a mouse maker, if one was found close enough to the click location, or nothing if no maker was close enough to the click location.
private handler UpdateMouseMarker() returns nothing    … previous code    if tDataPoints[tPointIndex] is not nothing then         … previous code          variable tRect as Rectangle         put rectangle [tPointX - kHitTestFudgeFactor,  tPointY  - kHitTestFudgeFactor, tPointX + kHitTestFudgeFactor, tPointY + kHitTestFudgeFactor] into tRect         if tMouseLoc is within tRect then             put [mData[1][tPointIndex],  tDataPoints[tPointIndex]] into tMouseMarker         end if      end if   … code continues   put tMouseMarker into mMouseMarker end handler
Handling OnMouseDown, OnMouseUp and OnMouseCancel
These handlers are a little more complicated as it is possible for the user to mouse down on one marker and then drag away.
If the user does mouse up away from the mouse down marker we need to send a markerMouseRelease event rather than a markerMouseUp. This is consistent with the mouseUp and mouseRelease messages in LiveCode Script.
In order to do that we need to store the mouse down marker in a separate instance variable to check in  OnMouseUp and OnMouseCancel.
The OnMouseCancel message is sent if the user drags right out of the widget while OnMouseUp  would be sent as long as they are in the widget which is why we need to handle both messages.
The OnMouseDown Handler
Add the OnMouseDown handler.
- Update the mouse marker.
- Check that there is a current mouse marker.
- Update the current mouse down marker (mMouseDownMarker) with the current mouse marker (we will use this when we handle OnMouseUp and OnMouseCancel).
- Post the makerMouseDown message to the widget object with the mouse down marker, mMouseDownMarker, as a parameter.
public handler OnMouseDown() returns nothing   UpdateMouseMarker()   if mMouseMarker is not [] then      put mMouseMarker into mMouseDownMarker      post "markerMouseDown" with mMouseDownMarker   end if end handler
The OnMouseUp Handler
Add the OnMouseUp handler.
- Update the mouse marker.
- Check that there is a current mouse down marker.
- Check if the current mouse marker(mMouseMarker) is the current mouse down maker (mMouseDownMarker). If so the user did not drag away from the marker after the mouse down so post the makerMouseUp message to the widget object with the mouse down marker, mMouseDownMarker, as a parameter.
- If the current mouse marker and mouse down marker are different the user dragged away from the marker after the mouse down so  post the makerMouseRelease message to the widget object with the mouse down marker as a parameter.
- Clear the mouse down marker.
public handler OnMouseUp() returns nothing   UpdateMouseMarker()   if mMouseDownMarker is not [] then      if mMouseMarker is mMouseDownMarker then         post "markerMouseUp" with mMouseDownMarker      else         post "markerMouseRelease" with mMouseDownMarker      end if   end if   put [] into mMouseDownMarker end handler
The OnMouseCancel Handler
This message is sent when something happens which should cause the previous mouse down action to be considered cancelled. For example, moving the mouse outside the widget area.
Add the OnMouseCancel handler.
- Check that there is a current mouse down marker
- Post the makerMouseRelease message to the widget object with the mouse down marker, mMouseDownMarker, as a parameter.
- Clear the mouse down marker.
public handler OnMouseCancel() returns nothing   if mMouseDownMarker is not [] then      post "markerMouseRelease" with mMouseDownMarker      put [] into mMouseDownMarker   end if end handler
Document the marker mouse messages
Don’t forget to add in-line documentation to your LiveCode Builder Code.
For a refresher on adding documentation look back to Lesson4: Step 7 – Documenting the Widget.
Test and Install the Widget
We will test the widget and, if we are happy, install it.
- Open the Extension Builder from the Tools Menu.
- Load the LCB file.
- Click Test and test out all the new properties and messages of the widget. Try adding a markerMouseUp handler to the graph.
on markerMouseUp pMarker   answer pMarker end markerMouseUp
- If you are happy with it click Install.
- Set the icons, available from the Resources for this lesson. This will differentiate it from the original Line Graph widget in the Tools Palette.
- Check the documentation shows in the Dictionary.