Responding to user actions
The Rotated Text widget responded to clicks by sending a mouseUp message.
We could do the same with the Pie Chart widget but it would be more useful to tell the user which segment had been clicked on.
To do this we will
- Work out which sector of the pie chart was clicked on
- Post a sectorClicked message with a pSectorNumber parameter.
Working out which sector was clicked
To work out with sector was clicked we will use some simple trigonometry. Â There are a few pieces of information and steps to find the clicked sector number.
- Get the location the user clicked at
- Check the click location is within the pie chart (not anywhere else in the widget)
- Calculate the horizontal and vertical distances from the origin of the circle to the click point
- Use these values to calculate the angle, the value will be between -90° and 90°
- Work out which quadrant of the circle was clicked in
- Calculate the angle as a value between 0° and 359°
- Iterate through each sector checking if the calculated angle is between its start and end angles
- Send a sectorClicked message with the clicked sector number as a parameter.
Using instance variables
There was some information used when drawing the pie chart that we need here.
- Chart origin
- Chart radius
- Start and end angles of each sector
We don’t have access to these values at the moment so we will update the code to use instance variables for these values. The values will be calculated and used in OnCreate and used in OnMouseUp.
Add 3 more instance variable declarations.
- mChartOrigin: a point
- mRadius: a number
- mSectorAngles: a list of lists. Each element being a list of 3 numbers, the start and end angles of the sector.
widget community.livecode.elanorb.piechart … module imports … metadata … existing instance variables private variable mChartOrigin as Point private variable mRadius as Number private variable mSectorAngles as List … code continues end widget
Update the points in the OnPaint handler that use these variables.
- Delete the tRadius declaration.
- Delete the tChartOrigin declaration.
- Update the line that sets the radius value to set mRadius
- Update the line that sets the origin value to set mChartOrigin
- Update the line that calculates the sector path using tRadius and tChartOrigin and ensure it uses  mRadius and mChartOrigin.
public handler OnPaint()   … previous code  Âvariable tRadius as Number variable tChartOrigin as Point  … other variable definitions  Âput tDiameter / 2 into tRadius Âput point [the width of tChartRect/2,the height oftChartRect/2] into tChartOrigin  put tDiameter / 2 into mRadius   put point [the width of tChartRect/2,the height of tChartRect/2] into mChartOrigin   repeat for each element tElement in mValues      … previous code     Âput sector path centered at tChartOrigin with radii[tRadius,tRadius] from tStartAngle to (tStartAngle+ tAngle) into tPath     put sector path centered at mChartOrigin with radii [mRadius,mRadius] from tStartAngle to (tStartAngle + tAngle) into tPath      … code continues   end repeat end handler
Next we need to add a line that stores the start and end angles of each sector in  mSectorAngles.
After drawing each sector add a line that pushes the list [tStartAngle,(tStartAngle + tAngle)] onto mSectorAngles.
The order of the elements in the list will correspond to the sector numbers.
public handler OnPaint()   … previous code   repeat for each element tElement in mValues      … previous code    fill tPath on this canvas      push [tStartAngle,(tStartAngle + tAngle)] onto mSectorAngles     add tAngle to tStartAngle   end repeat    … code continues end handler
The OnMouseUp handler
We want the widget to send a message when the Pie Chart is clicked.
To do this we handle the LCB OnMouseUp message and post a message to the widget object.
Start by declaring the OnMouseUp handler and variables.
public handler OnMouseUp()   variable tClickLoc as Point   variable tClickAngle as Number   variable tClickX as Number   variable tClickY as Number   variable tOriginX as Number   variable tOriginY as Number   variable tDeltaX as Number   variable tDeltaY as Number   variable tChartRect as Rectangle    … code continues end handler
Checking the click loc
Firstly we want to check the user clicked within the pie chart. If they clicked anywhere else in  the widget no message is sent.
- Get the click position. This returns a Point. A point is a list of 2 numbers, the x and y coordinates of the point.
- Get the rectangle of the pie chart
- Get the path of the circle centered at mChartOrigin with radius mRadius.
- Get the bounding box of the path. This returns the smallest rectangle that completely encloses a path.
- Check the click location is within the bounding box of the pie chart.
public handler OnMouseUp()   … previous code   put the click position into tClickLoc   put the bounding box of circle path centered at mChartOrigin with radius mRadius into tChartRect   if tClickLoc is within tChartRect then   … code continues   end if end handler
Calculating the angle
Next we work out which sector was clicked in. To do this we use some high school trigonometry. If you need a refresher have a look at http://www.bbc.co.uk/education/guides/zq4w7ty/revision.
The first step is to calculate the angle of the click location, relative to the origin of the chart.
To calculate this angle we need to find the horizontal and vertical differences between the click location and the origin.
To calculate the angle we use the arctangent because we know the length of the sides opposite and adjacent to the angle we want to calculate.
- deltaY = opposite
- deltaX = adjacent
- tanΘ = opposite/adjacent
- The angle is in radians so convert to degrees.
- Round the angle to get a whole number.
public handler OnMouseUp()   … previous code  if tClickLoc is within tChartRect then   … previous code    put the arctangent of (tDeltaY/tDeltaX) into tClickAngle    put tClickAngle * (180/pi) into tClickAngle    round(tClickAngle)    … code continues   end if end handler
The angle will be a value between -90° and 90°.
To convert the angle to a value between 0° and 259° we need to work out which quadrant of the circle the user clicked.Â
Quadrant | tDeltaX | tDeltaY |
Q1 | positive | positive |
Q2 | negative | positive |
Q3 | negative | negative |
Q4 | positive | negative |
Add the code to convert the angle to a value between 0° and 360°.
- The angle is positive so nothing needs to be done.
- The angle is negative to adding 180 gives an angle between 91° and 180°.
- The angle is positive so adding 180 gives an angle between 181° and 270°.
- The angle is negative so adding 360 gives a value between 271° and 259°.
public handler OnMouseUp()   … previous code  if tClickLoc is within tChartRect then   … previous code    if (tDeltaX >= 0) and (tDeltaY >= 0) then         -- Q1 - do nothing      else if (tDeltaX < 0) and (tDeltaY >= 0) then         -- Q2         add 180 to tClickAngle      else if (tDeltaX < 0) and (tDeltaY < 0) then         -- Q3         add 180 to tClickAngle      else if (tDeltaX >= 0) and (tDeltaY < 0) then         -- Q4         add 360 to tClickAngle      end if      … code continues   end if end handler
Finding the sector
Now that we have the angle of the click we need to find the sector it belongs to.
Iterate across each each element of mSectorAngles, remember that each contains the start and end angle of a sector and the elements are in order.
If tClickAngle is between the start and end angle of the sector this is the sector that was clicked in.
Once the correct sector has been identified assign the sector number to tSectorNumber and exit the loop.
public handler OnMouseUp()   … previous code   if tClickLoc is within tChartRect then   … previous code   variable tSector as List   variable tSectorNumber as Number   variable tClickSector as Number   put 0 into tSectorNumber   repeat for each element tSector in mSectorAngles         add 1 to tSectorNumber         if tClickAngle > element 1 of tSector and tClickAngle < element 2 of tSector then            put tSectorNumber into tClickSector             exit repeat         end if   end repeat   … code continues   end if end handler
Sending the message
Now the sector number has been found we want to tell the widget to send a message.
To send a message with parameters use the statement
post Message to Object [ with Arguments ]
Arguments is a list of arguments. In this case it is a list with one element but must still be contained in ‘[‘ and ‘]’.
public handler OnMouseUp()   … previous code   if tClickLoc is within tChartRect then   … previous code post "sectorClicked" with [tClickSector]   end if end handler
Test the widget
To test the widget:
- Open the Extension Builder from the Tools menu
- Load ‘pieChart.lcb’
- Click Test
- Select the widget
- Open the Code Editor
- Add a sectorClicked handler
on sectorClicked pSector   put pSector end sectorClicked