Now it is time to add some interactivity to our canvas. We want our users to be able to drag and drop the comic covers around and organize them as they want.
Let’s start with some properties we’ll need on the ABCanvas:
mLastX as integer ' our last X position mLastY as integer ' our last Y position DragElem as ABelement ' the element we're dragging
We are going to use some of the Canvas events to do the mouse handeling, but we still want those events to be available later so we have to declare them:
Event MouseDown(x as integer, y as integer) As boolean Sub () Event MouseDrag(x as integer, y as integer) Sub () Event MouseUp(X as integer, Y as integer) Sub () Event MouseMove(X as integer, Y as integer) Sub ()
Next we need some functions on the ABCanvas to refresh an ABElement, to find an ABElement and to bring this element to the front.
RefreshElement(): this function will refresh only the given element:
Sub RefreshElement(tmpElem as ABElement) Dim iLeft, iTop as Integer ' get the real left and right from our center x and y iLeft = tmpElem.x - tmpElem.w \ 2 iTop = tmpElem.y - tmpElem.h \ 2 ' redraw only me and all other ABElements we are covering DrawMe iLeft,iTop,tmpElem.w,tmpElem.h End Sub
In Tutorial 3 we learned how to find a mousedown in a irriguar shape. For this tutorial we’ll going to use only part of this method in ElementHit(). As all our objects are rectangles and we’ll know their positions we are going to find them the conventional way. To check if we’re in the shadow, we’ll use the method from tutorial 3.
Function ElementHit(x as integer, y as integer) As ABElement ' Find the ABelement hit by the point x,y (if any). Dim tmpElem As ABElement Dim i, halfw, halfh As Integer for i = UBound(MyElements) downTo 0 tmpElem = MyElements(i) if tmpElem.Visible then ' all ABElements x and y positions are in the center, so we take half the width and height to get the left and top halfw = tmpElem.w / 2 halfh = tmpElem.h / 2 ' Are we within the bounds of this picture? if Abs(x - tmpElem.x) < halfw and Abs(y - tmpElem.y) < halfh then ' yes we are, but we want it only if our mask is not transparent if ComicMask.Graphics.Pixel(Abs(x - tmpElem.x + halfw), Abs(y - tmpElem.y + halfh)) = &c000000 then return tmpElem end if end if end if next ' nothing found so we return nil return nil End Function
And then a function to bring the ABElement to the front if we click on it:
Sub BringToFront(FrontElem as ABelement) #pragma disableBackgroundTasks ' Bring the given ABElement to the front, so it's drawn on top of all others. Dim i As Integer Dim j as integer Dim maxi as integer maxi = UBound(MyElements) ' in reverse order for i = maxi downTo 0 if MyElements(i) = FrontElem then ' Found! Remove it from the list MyElements.Remove i ' and add it at the end MyElements.Append FrontElem Return end if next End Sub
Now we’re ready to add some interactivity to our canvas! Let’s start with changing our mouse cursor if we are above a comic cover. We’ll use the MouseMove() event of the canvas for this
Sub MouseMove(X As Integer, Y As Integer) ' if we are above one of our Elements, we change the mouse cursor to a little hand if ElementHit(X,Y) <> nil then self.MouseCursor = System.Cursors.FingerPointer else self.MouseCursor = System.Cursors.StandardPointer end if ' continue with the default MouseMove event MouseMove X,Y End Sub
Done! Let’s grab the cover and drag it a little around.
We start in the MouseDown() event of the canvas. We’ll search if we have hit an ABElement. If so we’ll remember our position, bring it to the front and set the DragElem to the found element:
Function MouseDown(X As Integer, Y As Integer) As Boolean Dim tmpElem as ABElement tmpElem = ElementHit(X,Y) if tmpElem <> nil then ' remember our current position mLastX = X mLastY = Y ' bring the found ABelement to the front BringToFront tmpElem ' refresh the element so it is redrawn RefreshElement tmpElem ' remember this ABElement so we can drag it around DragElem = tmpElem Return true end if ' continue with the default MouseDown event return MouseDown(X,Y) End Function
In the MouseDrag() event we’ll redraw our ABElement to the position of the cursor. Here a couple of things that need to happen:
- If our current position = our previous position, don’t redraw
- We add some extra pixels around the object to prevent ‘trailing’
- We calculate our new position
- As we draw our element to its new position, we also have to redraw the background and other objects on the old position.
- We remember our current position for the next pass
Sub MouseDrag(X As Integer, Y As Integer) #pragma disableBackgroundTasks if DragElem <> nil then if mLastX = X and mLastY = Y then ' no need to redraw, it is the same position as before Return end if Dim oldX, oldY, newX, newY, fullLeft, fullTop, fullWidth, fullHeight as integer Dim Extra as integer ' we add some extra pixels around the object when we redraw because otherwise we sometimes ge a 'trail' Extra = 5 ' we remember the old position oldX = DragElem.X oldY = DragElem.Y ' calculate the new position newX = oldX + X - mLastX newY = oldY + Y - mLastY ' Find the union between the old and the new position fullLeft = Min(oldX,newX) - DragElem.w \ 2 - Extra fullTop = Min(oldY,newY) - DragElem.h \ 2 - Extra fullWidth = Max(oldX,newX) - DragElem.w \ 2 + DragElem.w - fullLeft + Extra * 2 fullHeight = Max(oldY,newY) - DragElem.h \ 2 + DragElem.h - fullTop + Extra * 2 ' set our new position DragElem.X = newX DragElem.Y = newY ' redraw only what is needed DrawMe fullLeft, fullTop, fullWidth, fullHeight ' remember the last mouse position mLastX = X mLastY = Y end if ' continue with the default MouseDrag event MouseDrag X,Y End Sub
And last we handle the mouse up. Just to be sure, we do a last redraw of our element and we set the DragElem back to nil.
Sub MouseUp(X As Integer, Y As Integer) if DragElem <> nil then ' refresh one last time so we have the very last position RefreshElement DragElem ' set our DragElem to nothing DragElem = Nil end if ' continue with the default MouseUp event MouseUp X,Y End Sub
Ok, Let us run our program! We should be able to see by the mouse cursor if we’re above an Element. If we click on it, it will jump to the front and we can drag it around. A lot of stuff happended in this tutorial so if you have questions or suggestions, please leave a message.
Here is a small video to show what we made:
In our next lesson we’ll expand a little what we have learned so far so that we can play around with multiple types of objects.
Source code for this tutorial: http://www.gorgeousapps.com/Tut4.zip