In this next tutorial we’ll going to add a menu to our elements so the users can really interact with them.
The idea would be that when we click on an element, this element becomes active and gets a menu with a play and stop button. When we click on another element, the menu is removed from the previous element and opened on the new element. When we click on something else than an element, it disappears.
Let’s get started with the code:
We create a new class ABMenu that will hold our play and stop buttons. We give it a type and a picture.
X as integer Y as integer W as integer H as integer Image as picture Sub Constructor(tmpType as integer, tmpImage as picture) me.Type = tmpType me.Image = tmpImage End Sub Sub Destructor() CleanUp End Sub Sub CleanUp() Image = nil End Sub
In the ABelement, add a new property HasMenu. This way we can choose if an element has a menu or not. Also add a table that holds our MenuButtons.
Hasmenu as Boolean MenuButtons(-1) as ABMenu
Change the Init() function to set our HasMenu variable:
Sub Init(tmpID as integer, tmpType as integer, tmpX as integer, tmpY as integer, tmpInnerX as integer, tmpInnerY as integer, tmpImage as picture, tmpMask as picture, tmpGlow as picture, tmpVisible as boolean, tmpHasMenu as boolean ) me.ID = tmpID me.x = tmpX me.y = tmpY me.Visible = tmpVisible me.w = tmpImage.Width me.h = tmpImage.Height me.Type = tmpType me.MyGlow = tmpGlow me.MyMask = tmpMask me.InnerX = tmpInnerX me.innerY = tmpInnerY me.HasMenu = tmpHasMenu ' new Image = NewPicture(w,h, 32) image.Graphics.DrawPicture tmpImage,0,0 if MyMask <> nil then me.w = MyMask.Width me.h = MyMask.Height end if pBuffer = NewPicture(w,h, 32) gBuffer = pBuffer.Graphics DrawMe End Sub
A new function AddMenuButton() will allow us to add Menu buttons to each element:
Sub AddMenuButton(tmpType as integer, tmpPicture as picture) Dim tmpMenu as new ABMenu(tmpType, tmpPicture) MenuButtons.Append tmpMenu End Sub
Now for the changes in the ABCanvas class. We’ll add a new property CurrentMenuElement. This is similar to our DragElement from a previous lesson: it holds a temporary link to the element that currently has the menu.
CurrentMenuElement as ABElement
We make a change to our RefreshElement fuction because we want also to refresh the space that our menu is taking:
Sub RefreshElement(tmpElem as ABElement, Extra as integer) ' new Dim iLeft, iTop as Integer iLeft = tmpElem.x - tmpElem.w \ 2 - Extra ' changed iTop = tmpElem.y - tmpElem.h \ 2 - Extra ' changed ' redraw only me and all other ABElements we are covering DrawMe iLeft,iTop,tmpElem.w + Extra * 2,tmpElem.h + Extra * 2 ' changed End Sub
Two new functions will handle a click on a menu button and if we hover over a menu button. We go through our current menu buttons and see if the mouse is over one of them. In that case we can start or stop playing the CD, DVD or comic. The code to play things is not included here as it would take us to far from our Canvas tutorials. A google search or the RealStudio forum can help you with that.
' to handle a click on one of our menu buttons Function HandleMenuClick(X as integer, Y as integer) As boolean Dim a as integer dim tmpMenu as ABMenu if CurrentMenuElement <> nil then for a = 0 to Ubound(CurrentMenuElement.MenuButtons) tmpMenu = CurrentMenuElement.MenuButtons(a) if X >= tmpMenu.X and X <= tmpMenu.X + tmpMenu.W and Y >= tmpMenu.Y and Y <= tmpMenu.Y + tmpMenu.H then ' a button was clicked, handle what needs to happen (like start or stop playing) MsgBox "You pressed button " + Str(tmpMenu.Type) + " on element type " + str(CurrentMenuElement.Type) + " with ID " + str(CurrentMenuElement.ID) Return true end if next end if Return false End Function ' to change our mouse cursor if we are hovering above a button Function HandleMenuMove(X as integer, Y as integer) As boolean Dim a as integer dim tmpMenu as ABMenu if CurrentMenuElement <> nil then for a = 0 to Ubound(CurrentMenuElement.MenuButtons) tmpMenu = CurrentMenuElement.MenuButtons(a) if X >= tmpMenu.X and X <= tmpMenu.X + tmpMenu.W and Y >= tmpMenu.Y and Y <= tmpMenu.Y + tmpMenu.H then Return true end if next end if Return false End Function
Now for the main event: the function to show our menu around our active element. Once we’ve clicked a Element, the menu should be drawn around it. We’ll start with an orange frame around the Element and then we’ll draw our menu buttons. As all our elements have their own buttons, we iterate over the MenuButtons() table and draw them from right to left. We do this because we do not know how many buttons will be drawn and we want them right aligned.
Only when the menu is completely drawn we’ll push the changed part of the buffer to the canvas.
Sub ShowMenu(tmpElem as ABelement) Dim iLeft, iTop as Integer Dim a as integer Dim posX 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 ' draw our menu gBuffer.ForeColor = &cFF5E0F gBuffer.PenWidth = 3 gBuffer.PenHeight = 3 ' we do minus 5 because our shadow = 5 gBuffer.DrawRect iLeft - 2, iTop - 23, tmpElem.w + 3 - 5, tmpElem.h + 24 - 5 gBuffer.PenWidth = 1 gBuffer.PenHeight = 1 gBuffer.FillRect iLeft - 2, iTop - 23, tmpElem.w + 3 - 5, 24 ' and our buttons PosX = iLeft + tmpElem.w - 23 for a = 0 to Ubound(tmpElem.MenuButtons) gBuffer.DrawPicture tmpElem.MenuButtons(a).Image, PosX, iTop - 21 tmpElem.MenuButtons(a).X = posX tmpElem.MenuButtons(a).Y = iTop - 21 tmpElem.MenuButtons(a).W = tmpElem.MenuButtons(a).Image.width tmpElem.MenuButtons(a).H = tmpElem.MenuButtons(a).Image.height posX = posX - 20 next ' and draw the result to the canvas self.Graphics.DrawPicture pBuffer, iLeft - 3, iTop - 24, tmpElem.w + 6, tmpElem.h + 27, iLeft - 3, iTop - 24, tmpElem.w + 6, tmpElem.h + 27 End Sub
Let’s also write a function to hide our menu:
Sub HideMenu() if CurrentMenuElement <> nil then ' let's redraw ' we add some extra pixels around the object when we redraw because we want the menu removed RefreshElement CurrentMenuElement, 25 ' and we reset our CurrentMenuElement CurrentMenuElement = nil end if End Sub
Almost there! All we now have to do is handle the mouse down and mouse up event so that they can show or hide the menu, and if a menu button is clicked handle the button action.
First our MouseDown(). A very import part is that if we click on our canvas, we want to check if we are clicking a menu button. This has to be done first and before we’ll handle any other actions. If a menu button is handled, we can exit the function with return false to stop any other mouse events. If we click on the desktop, the existing menu is hidden.
Function MouseDown(X As Integer, Y As Integer) As Boolean ' the first thing we do is checking if we clicked on a menu if HandleMenuClick(X,Y) then ' it is handled Return false end if Dim tmpElem as ABElement tmpElem = ElementHit(X,Y) if tmpElem nil then ' does someone have a menu that is now visible? HideMenu ' new if IsContextualClick then ' right mouse button ' bring the found ABelement to the front BringToFront tmpElem ' group them together GroupMyType tmpElem.type, X, Y, 500 return false else ' left mouse button ' 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, 0 ' remember this ABElement so we can drag it around DragElem = tmpElem Return true end if else ' does someone else have a menu that is now visible? HideMenu ' new end if ' continue with the default MouseDown event return MouseDown(X,Y) End Function
In our MouseUp() event we’ll show our menu.
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,0 ' set our DragElem to nothing DragElem = Nil end if Dim tmpElem as ABElement tmpElem = ElementHit(X,Y) if tmpElem <> nil then if IsContextualClick = false then ' left mouse button ' show our menu, if any if tmpElem.HasMenu then ShowMenu tmpElem ' remember this ABelement having the menu CurrentMenuElement = tmpElem end if end if end if ' continue with the default MouseUp event MouseUp X,Y End Sub
And in the MouseMove() event we’ll handle the mouse cursor if we are hovering above a menu button:
Sub MouseMove(X As Integer, Y As Integer) ' first check if we are over a menu if HandleMenuMove(X,Y) then self.MouseCursor = System.Cursors.FingerPointer Return end if ' 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
Finally we’ll change the LoadType() function in the Main form to add our menu buttons to our elements. The CD and DVD we’ll give a play and stop button, the Comic only a play button. Here is the changed code:
tmpElem.Init(Counter,tmpType, rnd * (me.Width - tmpPic.width) + tmpPic.Width / 2, rnd * (me.Height - tmpPic.Height) + tmpPic.Height / 2,InnerX,InnerY, tmpPic, tmpPicMask, tmpPicGlow, true, true) ' new we use true to add a menu ' and add the menu items ' load our play button for our menu select case tmpType case 1 ' a comic has only a play button Menuf = GetFolderItem("PlayButton.png") tmpElem.AddMenuButton(1, menuf.OpenAsPicture) case 2,3 ' a CD and DVD has a play and stop button Menuf = GetFolderItem("PlayButton.png") tmpElem.AddMenuButton(1, menuf.OpenAsPicture) Menuf = GetFolderItem("StopButton.png") tmpElem.AddMenuButton(2, menuf.OpenAsPicture) end select
Phew, Done! Let’s run our application with the added menu. It should work like this:
As always you can download the source code for this tutorial:
Until next time!