How to call a function or command in another object?

The message path is one of the fundamental concepts of LiveCode and describes how messages flow between LiveCode scripts. Sometimes, however, a programmer may wish to break out of the current path.  This lesson describes the basics of the message path and how to call handlers that are outside of the message path.

Understanding the Message Path

Understanding the Message Path

The message path defines how messages are handled within LiveCode. For example, which object should get the "mouseUp" message or how a button script communicates with a card script.

Each message, or handler call, moves up through the message path until an appropriate handler is found that can deal with the message. Consider a "mouseUp" message. The programmer may choose to handle this at varying levels:  Possibly in the script of button that was clicked, maybe in script of the card in which the button sits, or even in a library stack. The "mouseUp" message keeps climbing up the message path until it finds a handler that can handle it.

The order in which messages climb this path is fixed, see the diagram. The first rung of the ladder is front scripts. If no handler is found there, then we proceed onto object scripts. The next step if no handler is found there, is to look in the scripts of any groups the object belongs to. Then the card on which the object sits. Next background script, followed by the stack script of that card.  Then the main stack script of any parent stacks.  Then the scripts of any library stacks before checking any back scripts. If the messages was not handled at this point, then it is handled by the engine.

If an appropriate handler for the message is found, then the message stops climbing, unless the handler chooses to pass the message, in which case the climb continues. This facet allows the programmer to handle messages on multiple levels. For example, consider a status change message. We can handle this at an object level where we may change some text to reflect this new status.  We can then pass this message on, where at card level we may choose to update the background color. We may choose to pass the message on again allowing us to carry out any reaction to the new status at stack level.

Messages can be introduced at any level of the message path. For example, "preOpenCard" is sent at card level. Message only climb up the path, so a handler call at group level is not able to find its way down to an object.

Escaping the Message Path

Consider the following situation. We are writing a handler in a stack script. From this handler we wish to call a command called "myCommand"  that is defined in the script of a card called "myCard".  We cannot call this command in the traditional manner, since the card script is below the stack script in the message path. We need to escape the message path. To do this, we can use the LiveCode command "send".

call "myCommand" of card "myCard"

The call command allows us to specify the command we wish to invoke and the object in the message path it is sent to. The command is then propagate through the message path as before until it is handled. The context in which the target command is executed is that of the parent handler which called it. For example, if the target command makes reference to a field named "myField", this field is evaluated in the context of the parent handler. This could be an issue if you are calling a command on a different card, with both the parent and target cards having fields named "myField". Here the field used is that of the parent card, even though the command itself resides on the target card.

If we wish the target command to execute in the context within which it is defined, we can use the LiveCode command "send".  When a command is invoked via send, the context is temporarily switched to that of the command being called.

send "myCommand" to card "myCard"

Calling Functions

The call and send commands only work with commands. These are handlers that do not return a value. If we wish to invoke a function (outside of the current message path), we need to use the LiveCode function "value". The "value" function takes two parameters, the name of the the function we wish to invoke and an object in the message path. So, if we want to invoke a function in a group called "myGroup" on a card called "myCard" and put the value returned into a variable, we can use the following code:

put value("myFunction()", group "myGroup" of card "myCard") into tResult

Passing Parameters

So far, we have only considered handlers which do not take any arguments. In order to invoke handlers with parameters via "call", "send" or "value", we simply alter the name of the target handler to include parameters we wish to pass. For example, if we wish to call our command with parameters "hello" and "world":

call "myCommand hello world" of card "myCard"

Functions are also handled in much the same way, remembering to surround any parameters with parentheses.

put value("myFunction(hello, world)", group "myGroup" of card "myCard") into tResult

We can also include variables as parameters by appending their contents to the handler name:

send "myCommand" && tParam1 & comma && tParam2 to card "myCard"

Beware that if any of our variables contain comma separated values, these values are evaluated as separate parameters. To circumvent this, we need to surround our variable contents in quotes:

send "myCommand" && quote & tCSV & quote to card "myCard"

Similarly, we should also take care to enclose all multiword strings in quotes. Since we are writing our variable contents to string before using "call", "send" or "value", arrays do not function as expected (unless we serialize them to string in the parent handler and de-serialize in the target handler). We can use the LiveCode command "dispatch" to work around this issue.

Using Dispatch

The "dispatch" command provides a cleaner alternative to "call", "send" and "value" handlers described above. The "dispatch" command works in much the same way but has a few subtle differences. First of all the basics. Consider our first example, calling "myCommand" on "myCard":

dispatch "myCommand" to card "myCard"

As with "send", "dispatch" temporarily switch context when the target handler is executing.

We can also use dispatch with functions, with the value returned by the function being found in "the result".

dispatch function "myFunction" to card "myCard"

put the result into tResult

Parameters can also be passed using the "with" extension. Unlike the "call", "send" and "value" handlers, the parameters are sent directly to the target handler, rather than being extracted from the string used to call the handler. This negates the need to surround our parameters in quotes and also allows us to pass arrays:

dispatch "myCommand" to card "myCard" with "Hello World", tArray, tCSV

Unlike the "call", "send" and "value" handlers, which throw an error if they cannot find the target handler, "dispatch" reports the outcome of executing the target handler in the "it" variable. The value is one of the following:

• handled - the message was handled and not passed

• unhandled - no matching handlers were found

• passed - the message was handled but passed by all handlers

10 Comments

John Collett

When you say "out-with" do you mean "without"? If so, why not say "without"? Or is there something more subtle going on here?

Elanor Buchanan

Hi John

By "out-with the message path" we mean handlers that are not in the normal message path, perhaps a handler on another card or a substack of the main stack.

I hope that is a little clearer.

Elanor

Michael Pitkin

I've been using the Value() function for a couple of years now and it is a fantastic little utility especially with the constraints of the script limits only allowing 10 back and front scripts and 50 library stacks.

It helps as a great way to produce a plugin system, a plugin can register with your main stack to receive an alert without being in the front or back scripts and thus when the main stack receives a mouseUp event, it can alert the plugin.

But I didn't know about the Dispatch command, so I might experiment with it slightly and see what happens.

Salman

can you put multiple functions in a graphic object

Hanson Schmidt-Cornelius

Hi Salman,

sure you can add functions and commands to the script of a graphic object, just like you would functions and commands to card or stack scripts.

Kind Regards,

Hanson

paulo gomes

Hi,
Nice article that shows all different ways to execute a script in a handler. However, how can you launch a Card passing parameters?

The sintaxe

go to card "xxx" with "zzzzzz"

compiles but there is no way to capture de value "zzzzzz" in the card's handlers.

Thanks,
Paulo

Hanson Schmidt-Cornelius

Hi Paulo,

well, there are several ways to do this and here are two possible suggestions:

1. You could use a custom property to store the value that you want to pass. You could set this value before moving to the particular card and then use the "openCard" message to check if the value is set, read it and then set it to empty.

2. You could create your own command that takes the card ID and the value you would like to pass to the card. You can then also write the event handlers that process that information for you.

Kind Regards,

Hanson

Larry

VERY helpful article! IMHO the best thing LiveCode has going for it is the community of helpful programmers. Anyway, above you give this line: call "myCommand" of "myCard"

That didn't work for me. I had to use: call "myCommand" of card "myCard"

Larry

VERY helpful article! IMHO the best thing LiveCode has going for it is the community of helpful programmers. Anyway, above you give this line: call "myCommand" of "myCard"

That didn't work for me. I had to use: call "myCommand" of card "myCard"

Elanor Buchanan

Hi Larry, thanks for pointing out that oversight. The lesson has been updated.

Thanks

Elanor

Add your comment

E-Mail me when someone replies to this comment