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.