02-Feb-2007 Author: Anton Rolls Here's a quick tutorial showing how to implement the WORDS dialect in your VID style. Ok, so I am going to assume you are basing your new style (let's call it "fat-box") on the simplest VID style available, FACE. stylize/master [ fat-box: FACE 100x100 with [ init: [] ] ] To get it going we need to specify, at a minimum, the SIZE and the INIT block. That keeps LAYOUT happy. How does it look?: view layout [fat-box] Quite invisible-looking, but it's there. (We can tell by the disturbance of the light of distant stars near the edges of the new face.) Let's add a WORDS dialect to specify some options and parameters. stylize/master [ fat-box: FACE 100x100 with [ init: [] words: compose [ ghostly (func [new args][ new/effect: [merge luma 20] args ]) ] ] ] The WORDS dialect is expected to be a block of: - a word or several words followed by - a function (which is associated with the previous word or words). So in the above example I have defined a dialect with one word, GHOSTLY, in it. Now, inside the layout spec, I can follow the FAT-BOX style keyword with the GHOSTLY word and the above function will be called. How does it look?: view layout [fat-box ghostly] Now the ghostly form of the FAT-BOX can be seen... When does the WORDS block get processed? We can find out by printing some debug comments: stylize/master [ fat-box: FACE 100x100 with [ init: [print "INIT"] words: compose [ ghostly (func [new args][ print "WORDS dialect" new/effect: [merge luma 20] args ]) ] ] ] view layout [fat-box ghostly do [print "after FAT-BOX"]] This is printed: WORDS dialect INIT after FAT-BOX So we can see that the WORDS dialect is processed before INIT. Hmm... maybe I should reverse the order I've written it in the above style spec so it's more intuitive (ie. read order of execution from top to bottom.). Ok, let's talk about how the function is processed. The function has two arguments NEW and ARGS. NEW is the new FAT-BOX face instance that is being created by LAYOUT. ARGS is a block of values ("facets") collected after the FAT-BOX style word. stylize/master [ fat-box: FACE 100x100 with [ words: compose [ ghostly (func [new args][ ?? args new/effect: [merge luma 20] args ]) ] init: [] ] ] view layout [fat-box ghostly] In this example, what makes the GHOSTLY function unique and "useful" is that it sets the EFFECT facet so that the face becomes translucent. The function returns ARGS. The WORDS dialect block processor (inside the LAYOUT function) will advance the ARGS block automatically so it is positioned after the GHOSTLY word, and if there was a word following, it would be ready for that. However, let's now modify the example so GHOSTLY can be followed by an integer, which will specify the LUMA amount: stylize/master [ fat-box: FACE 100x100 with [ words: compose [ ghostly (func [new args /local amount][ ?? args if integer? args/2 [amount: first args: next args] new/effect: compose [merge luma (any [amount 20])] args ]) ] init: [] ] ] view layout [fat-box ghostly 8] Ok, so effectively we have added a parameter to the GHOSTLY keyword. When we accept a value, we should "consume" it, by advancing ARGS by one position. (This is achieved by the expression ARGS: NEXT ARGS.) Now let me show another feature of the WORDS dialect; several keywords can share the same function. So to demonstrate this I will add another keyword "TRANSLUCENT", which will be interpreted in exactly the same way as GHOSTLY. stylize/master [ fat-box: FACE 100x100 with [ words: compose [ translucent ghostly (func [new args /local amount][ if integer? args/2 [amount: first args: next args] new/effect: compose [merge luma (any [amount 20])] args ]) ] init: [] ] ] view layout [fat-box translucent 8] The function could determine which keyword was used simply by examining ARGS/1. And now let's add another keyword "THICK-EDGE". stylize/master [ fat-box: FACE 100x100 with [ words: compose [ thick-edge (func [new args][ ?? args new/edge: make new/edge [size: 10x10 color: none effect: [grid 3x3 0x0 maroon]] args ]) translucent ghostly (func [new args /local amount][ ?? args if integer? args/2 [amount: first args: next args] new/effect: compose [merge luma (any [amount 20])] args ]) ] init: [] ] ] view layout [fat-box translucent 28 thick-edge] You can see that the keywords can be specified in any order. Even though, in the WORDS block, the THICK-EDGE keyword and function were written before the TRANSLUCENT (and GHOSTLY) keywords, I was able to specify them in the opposite order in the LAYOUT spec block.