revUp - Updates and news for the LiveCode community
Issue 144 | November 23rd 2012 Contact the Editor | How to Contribute

OOP for LiveCode - Part Two
The second part of this hands on exploration of object orientation for LiveCode

by Hakan Liljegren

Part 2 - Setting classes for controls + more

You can download all the associated stacks and samples for this article here.

If you followed along in the previous article about the OOP-engine in LiveCode you might have noticed that it is possible to create new objects. If you also checked out the particles example you noticed that you can also create new objects that behave according to their class. BUT if you tried to create a control and apply a class to it you might have thrown your newly created OOP-engine in the waste-basket. (Well, if you haven't dug into the code of the OOP-engine stack itself of course.) If you need to set the class of an already existing control you can't just set the behavior of the control to the newly created object as behaviors are buttons and our class instances are groups.

But what if we could place the control into the most inner group of our instance? Lets add it to our OOP-engine:

on setClassForControl pControlID, pClass
   if exists(pControlID) then
      lock screen
      # First create class container
      put newObject(pClass) into tInstance
      # Now move control into the newly created instance
      cut pControlID
      start editing tInstance
      paste
      stop editing tInstance
      unlock screen
   end if 
end setClassForControl

Now try the following:

Create a new stack, and add a new card named "Classes" (as our OOP-engine searches for a card "Classes") On that card we create a button "Boldify". Now add the following code to the "Boldify" button:

on mouseEnter
   set the textStyle of me to "bold"
end mouseEnter

on mouseLeave
   set the textStyle of me to empty
end mouseLeave

Create a new button and name it "BoldifyAndRedify". This class will be a subclass of the "Boldify" button so we add:

getProp parent
   return "Boldify"
end parent

At the top of the script, then we need to override the mouseEnter and the mouseLeave:

on mouseEnter
   set the textColor of me to "red"
   dispatch "mouseEnter" to the owner of me # Call parents \
         handler
end mouseEnter

on mouseLeave
   set the textColor of the me to empty
   dispatch "mouseLeave" to the owner of me # Call parents \
         handler
end mouseLeave

Notice that as in almost all OOP we need to call the parents handler, and in LiveCode we can do that by using "the owner of me".

Now go back to first card and add a button "My Button", then execute the following line in the message box:

setClassForControl the long id of button "My button", \
      "boldifyAndRedify"

Now drag the mouse pointer in and out over the button and you should see it turn both bold and red when the pointer is over the button.

If you experiment with this you will probably soon notice that if you set the fontStyle of the button to be "bold" (by mistake or by intention) you will notice that you now only get the red effect. To solve that you need to set the textStyle of the button back to empty. Select "plain" in the "Text" menu. Bug? No consider it a feature instead! You can add several effects to your classes but easily override the ones you don't want by setting the properties in the property window.

To wrap things up, it would be handy to have some more convenience functions in our OOP-engine. Add the following code to the engine:

function isObject pObject
   if exists(pObject) and exists(the behavior of pObject) then
      if exists( button (the short name of the behavior of \
            pObject) of card "Classes") then
         return true
      end if
   end if
   return false
end isObject

function className pObject
   if isObject(pObject) then
      return the short name of the behavior of pObject
   else
      return empty
   end if
end className

function isObjectOfClass pObject, pClass
   if ClassName(pObject) is pClass then
      return true
   else
      return false
   end if
end isObjectOfClass

function messageObject pObject, pMethod
   put empty into tParams
   repeat with i = 3 to the paramCount
      do ("put param(i) into tArr" & i)
      put "tArr" & i & comma after tParams
   end repeat
   delete last char of tParams
   put "dispatch function" && quote & pMethod & quote && "to" \
         && pObject && "with" && tParams into tCommand
   do tCommand
   if it is "handled" then
      return the result
   else
      # Try command instead of function
      put "dispatch " & quote & pMethod & quote & " to " & \
            pObject & " with " & tParams into tCommand
      do tCommand
      if it is "handled" then
         return the result
      else
         throw "Object error: No method " & pMethod & \
               " in class " & className(pObject)
      end if
   end if
end messageObject

All these functions must be called with the long id of the object. The functions are hopefully mostly self-explanatory (well except for the messageObject I guess):

isObject pObject 

Returns true if the group is an "object"-group

className pObject 

returns the name of the class of the object

isObjectOfClass pObject, pClass

Returns true if pObject is of class pClass and false otherwise

messageObject(pObject, pMethod [param1, param2, ...])
This is a convenience function that calls the method of an object and can be used instead of "dispatch ..." followed by "put the result into ...". It also handles both functions and procedures. So if you have the following code in your class:

function login pUserName pPassword
   # Login towards the server and return true if everything
   # went OK
   # ...
   return true
end login

on logout
   # Logout the user and return true if everything went OK
   # ...
   return true
end logout

Now if you have created a player object "_player" you can do the following:

if messageObject(_player, "login", field "userName", field \
      "password") then
   # login successful
end if

And do the same for "logout" even though it is a command, i.e. uses "on"

if messageObject(_player, "logout") then
   # logout successful
end if

Happy Coding!

Hakan

About the Author

Hakan Liljegren lives in Karlstad, Sweden. He soldered his own computer in 1981, and has not stopped programming since. When not writing LiveCode apps for his company Exformedia he loves to ride his bike. Even in the winter!

 

 

 

Main Menu

What's New


Get 30% off on Black Friday