Introduction

updated: 05-01-2004 (23:07 GMT)
author: tkelly32@btinternet.com

required files:
- tutorial_skin.png
- tutorial_skin.ski
(install these to your /components/foo_looks folder)

This is a basic look tutorial, written to gently introduce you to foo_looks 2.0 look design process. It requires no previous experience with foo_looks.

If you are planning to learn & implement the LUA scripting language (recommended) we expect you to have a little knowledge of 'C' or 'Pascal' style programming techniques.

The tutorial is intended to be followed from start to finish. If you would prefer to have a reference type of guide, then try looking at the reference section of the site.

please note that the skin we will develop together is neither beautiful, nor complete. It's just a basic design template that i can use to show you the ropes.

note - there is an accompanying glossary for all of the new terms used in this tutorial. if a word is bold, then it means there is an entry in the glossary for it. click the link to pop up the definition.

part 1: Graphics

perhaps the best way to learn how foo_looks builds it's looks is to examine the source graphics that it uses to construct them.

here is the source graphics for my fooAMP look, purely as an example:

the first thing you should notice is that all of the buttons, and non-static parts of the look, have been seperated from the main section, or 'look core' as i prefer to call it.

when we start coding the look, we will be using the foo_looks syntax to place these buttons and other pieces back over the look core.

the reason we seperate the two is because it gives us freedom over positioning of objects. it means that we can move the play button around until it's absolutely perfectly positioned, without having to go back to Adobe Photoshop just to make a few pixel changes. the other advantage is that parts of our look can be made visible/invisible at different times, and we can code advanced looks that can even change the buttons currently displayed depending on what you are doing.
This dynamic type architecture is something previously unsupported by look interfaces, and is very exciting. we will talk about it more in more advanced sections.

the second thing you should notice, or that i would like to draw your attention to, is the fact that all of the buttons have been arranged into columns, which is important, and is how you will place the various states for each button when you eventually design your own look.

for example, look at the top-right corner and notice how each playback button has an accompanying 'pressed' graphic, or 'state', located directly beneath it.

note: it is not important which order you arrange the various states, but it is imperative that there is no unnecessary gaps between each state in your graphics. eg:

each button graphic can have from 1-10 different states. we will discuss the more uncommon states in a later section.
in practice however, most buttons you will design will have only 3 states, which are the classic Normal, Rollover, & Pressed states. This is something which most of you should be very familiar with.

You may have noticed that i only have 2 states for the playback buttons in my fooAMP look. This is because i only wished to have press effects, and no change on rollover - so i only needed 2 states for each button: Normal & Pressed

part 2: SKI code

2.1 examining the existing code for 'tutorial_skin.ski'

the ski file is where all of the code pertaining to your own individual look goes.

For the rest of this tutorial, i will always be referring to the tutorial files that you should have downloaded before starting this tuorial.

required files:
- tutorial_skin.png
- tutorial_skin.ski
(install these to your /components/foo_looks folder)

Our ski is pretty bare at the moment. In fact, if you were to try using the look as it is (which i presume you have) you will see exactly what the code does so far.

So far our code is only sufficient to set up the size of our look, and fill the rectangle with our 'look core'.

the only other thing it does so far, is to attach the 'draglook' script to our background, which allows us to drag the look around the desktop by holding down the mouse button and dragging anywhere inside the look.

If you now open the 'tutorial_look.ski' you will find the code that does this. in fact, this is the absolute minimum code required by every look you will design from now on.

note: .ski files are just simple text files. So you can use any text editor to read & edit them. My suggestion is to use either Notepad, or an editor such as UltraEdit

tutorial_look.ski

section lookinfo
string name "foo_looks 2.0 tutorial look"
string author "tk32"
string date "Jan-03-2003"
double version 1.0
string description "foo_looks 2.0 tutorial look"
string URL "http://www.btinternet.com/~sean.m.kelly/foo_looks/"

import foo_looks_common.ski

section colors

section globals

section window
int width 271
int height 103

sprite background
string image "tutorial_look.png"
int width 271
int height 103
list statemap states.None
list scripts string { "draglook" }

for the rest of this tutorialwe will be using this initial code, and our graphics, to create a fully working look.

First of all, let's look at the existing code in greater detail:

Our look is presently divided into 4 x 'section' blocks, 1 x 'sprite' block, and 1 x 'import' statement (which we will describe later). the best way to distinguish between the two is to think the following:

the average look will have about 4 sections, and roughly 10-20 sprites. however, as with most things in foo_looks, the only limit is your imagination, and looks can have unlimited numbers of sections & sprites.

section lookinfo

section lookinfo
string name "foo_looks 2.0 tutorial look"
string author "tk32"
string date "Jan-03-2003"
double version 1.0
string description "foo_looks 2.0 tutorial look"
string URL "http://www.btinternet.com/~sean.m.kelly/foo_looks/"

the first defined 'section' is name lookinfo, and is where you put the details of the look. all looks are advised to contain this short description of what they are, and who they are authored by. Anyone here with programming experience should be familiar with this practice of tagging their code.

i won't waste much time on this section, as you just fill in the fields with your own details. the reason we write the look info as variables, instead of just using comments, is because we can refer to any of these variable later in our code. an example of this might be to have the look version number display in the bottom right-hand corner of the look.

import foo_looks_common.ski

the import statement tells foo_looks that we wish to import the standard scripts from the file named foo_looks_common.ski. This file contains a list of useful standard scripts that can be used by any look designer. if anyone here has experience in programming, you might be better to think of this as the classic #include <class.h> statement in c/c++ programming.

the only reason we are using scripts at such an early stage, is because we want to be able to drag our look around the screen, and you need to use a basic script called "draglook" in order to do this. i will explain more when we discuss 'sprite background'.

section colors
section globals

section colors

section globals

these two sections will prove very useful as we work on our look, but for now they are empty, so i will spare you the explanantion until they are required

section window

section window
int width 271
int height 103

this section describes the properties of our look window. as we work through the tutorial look, it is likely that we will add more code to this section, but the only code that must be present at all times is the looks width & height in pixels.

note: this is not the size of your graphics file, but only the size you wish your look to be when on the desktop

sprite background

sprite background
string image "tutorial_look.png"
int width 271
int height 103
list statemap states.None
list scripts string { "draglook" }

now we reach our first 'sprite'. the fact that it is a sprite means that it will be used to display something on our look. sprites can display any of the following visual features:

every sprite you code can display any or all of the above visual features.

Later in the tutorial we will experiment with changing the order and visibility of these different features.

sprite background, as mentioned in a previous paragraph, is used to display our look core in the look window. if we didn't display a background, our look would just be a black rectangle.

let's look at each line of the sprite code:

string image "tutorial_look.png"

this line tells the sprite which file to use if it needs to display graphics.

int width 271
int height 103

these 2 lines tell the sprite how big we want it to be.. the full size of the look. notice that the width & height of this script is identical to the width & height in 'section window'.

now we'll make our first adjustment to the ski code.

because we always want our background to be the same width & height as the look window, i will show you how to use the 'section window' values as reference.

open 'tutorial_look.ski' and change the following:

sprite background
string image "tutorial_look.png"
int width 271
int height 103

into...

sprite background
string image "tutorial_look.png"
int width window.width
int height window.height

this tells sprite background to use the numbers we have already defined in 'section window'. now that we have done this, 'sprite background' will always have the same dimensions as our look, and if we decide to change the look's width & height at a future date, the background size will automatically increase with it.

referring to values that have already been set can be a really useful feature. in a few paragraphs i will show you how we can use this same principal when using different text & fill colors.

list statemap states.None

the statemap is our way of setting how we would like our graphics to respond to mouse movements and clicks.
F for example, the statemap is used when we want our buttons to respond to mouse rollover, or mouse press actions.
As we don't wish our background to change when the mouse moves over it, we have set the statemap to the preset 'none'.
When setting the statemap, you can use the pre-defined presets, or give literal values.

for your information, the states.None preset is the equivalent of using -

list statemap int { 0 0 0 0 0 0 0 0 0 0 }

we will discuss the statemap in detail when we work on adding buttons to our look.

list scripts string { "draglook" }

For the last line of 'sprite background' we have a line of code that attaches an LUA script to our sprite.

What is an LUA script??
An LUA script is a section of code that allows us designers to use advanced features in our looks. LUA scripts are most often used to code special types of buttons and animations.
the design team have written a number of ready-to-use scripts in a file called foo_looks_common.ski, which you can use in your own looks.
We hope that some of you will be interested in learning how to script for yourselves. It's great fun and unlocks so many new & exciting features to your looks.

As i described in a previous paragraph, we used the import command to add the common scripts to our look. one of those common scripts is called 'draglook', and when you attach this script to a sprite, you can click & drag the look around the desktop. As with the statemaps, we will not be discussing this feature in great detail until we need to, so don't panic if you don't understand it yet.

2.2 Adding a new sprite

now that you have learnt the difference between sections & sprite, we will add our first custom sprite.

the first custom sprite i want you to add will be used to display the track details for the current track. We will also be using the popular TagZ code to help us format the text exactly how we like it.

the first thing you need to do is go to the end of the existing code, and start typing your new sprite.

Add this code to your file:

sprite trackInfo
list rect int { 15 15 235 15 }
string fspec "%artist% - %title%"
list fontcolor int { 255 12 48 69 }

It's perhaps a good time now to discuss how to name your sections & sprites.

sprites and sections can have any name you desire, except the following:

lookinfo
window
globals
states
texthint
fontstyle
color
texthint
render
align
gradientmode
trimming
spectrum

all of the above are pre-defined or reserved sections.

note - only 1of the above is a sprite. 'sprite spectrum'

names can be mixed case and include numbers and special characters but no spaces are allowed

on to the next line:

The Rect.

list rect int { 15 15 235 15 }

the rect is how we define the coordinates & size of our sprites in pixel values. the location is always relative to the top-left corner of the look, which is considered 0,0.
Using rect is a shorthand equivalent to the following (which does exactly the same thing, but takes longer to type):

int x 15
int y 15
int width 235
int height 15

so you have probably guessed how the numbers in rect are ordered:

list rect int { x y width height }

note: if you decide to use rects, you must always declare all 4 values.

next, we set the text display of the sprite:

string fspec "%artist% - %title%"

this line tells foo_looks what text we want to display for our sprite. i have used very very basic 'tagZ' formatting here to get the fields for the current artist & title. At the moment, these values are only set when a track is playing.

when we cover fspec's in detail later, we will talk more about what options we have for displaying tagZ formatting.

finally, we set a colour for our text:

list fontcolor int { 255 12 48 69 }

this code sets the colour of our text to a dark blue shade. if we didn't include this line our text would be invisible. the numbers represent the following colour values:

list fontcolor int { Alpha Red Green Blue }

for now, please keep the alpha value as 255, i will demonstrate the alpha values later.

Here are some common colors to help you get started:

Red: 255 255 0 0
Green: 255 0 255 0
Blue: 255 0 0 255
White: 255 255 255 255
Black: 255 0 0 0

in fact, wouldn't it be nice if we could call colors by their names, instead of using this ugly ARGB method.

that's exactly what section color was designed for!

ok, now scroll up to section color, and type the following directly beneath it:

section color
list darkblue int { 255 12 48 69 }

you have now set our custom color as a preset.

now go back to sprite trackInfo and change the following:

sprite trackInfo
list rect int { 15 15 235 15 }
string fspec "%artist% - %title%"
list fontcolor int { 255 0 0 0 }

into..

sprite trackInfo
list rect int { 15 15 235 15 }
string fspec "%artist% - %title%"
list fontcolor color.darkblue

notice how we are referring to an existing variable, just like we did when we told sprite background to get it's width & height from section window

you can add as many color presets as you like.

note: the following colors are already defined by default: white, black, gray, green, red, blue, null(invisible)

it's an awesome way to make the code a little more easy to read. only the most experienced designers can visualize a colour based on it's ARGB values.
The other advantage is that reusing the same colors throughout your skin helps you to achieve continuity for your color scheme.

at this point, i would like you all to try loading your looks again and see how the new sprite you have just written looks.

now we're going to change the size & font of the text.

add the next 2 lines to your track info sprite:

sprite trackInfo
list rect int { 15 15 235 15 }
string fspec "%artist% - %title%"
list fontcolor color.darkblue
int fontsize 9
string fontname "Arial Black"

fontsize sets the pixel size of the font, and fontname gives the name of a font that has been installed on your PC.

now we will learn how to make these text settings global.

we want all of our sprites to have the same text color, size & font - so we move these settings up into section globals.

move the last 3 lines from sprite trackInfo to section globals

sprite trackInfo
list rect int { 15 15 235 15 }
string fspec "%artist% - %title%"
list fontcolor color.darkblue
int fontsize 9
string fontname "Arial Black"

cut & paste to...

section globals
list fontcolor color.darkblue
int fontsize 9
string fontname "Arial Black"

now we have done this, every sprite we code will start with these default settings. However, this does not restrict you, and you are very welcome to change any or all of them for each sprite.

now you understand the purpose of section globals i would like you to make 2 more global settings. I want you to put the filename and statemap settings from sprite background into the globals section.

move the following 2 lines into globals, like we did just now

sprite background
string image "tutorial_look.png"
int width 271
int height 103
list statemap states.None
list scripts string { "draglook" }

cut & paste to....

section globals
list fontcolor color.darkblue
int fontsize 9
string fontname "Arial Black"
string image "tutorial_look.png"
list statemap states.None

now we have set filename and statemap globals.

Note: global settings are only used if you do not set that variable in your sprite. for example, if you write another sprite and add the line int fontsize 14, then it will ignore the global fontsize of 9

your skin will now look something like this:

 

2.3 sprite playbutton

now it's time to create our first button. I'll give you the code for it first, then we can discuss it line by line.

sprite playbutton
list rect int { 110 73 31 24 }
list srcoff int { 285 12 }

you should understand the rect by now.. so let's discuss this new code.. srcoff

Srcoff

list srcoff int { 285 12 }

what this code does, is tell foo_looks the coordinates of the button graphics. it's an abbrieviation of the word 'source offset', and gives pixel coordinates relative to the top-left corner of the graphics file.

Note: we would normally have to specify the graphics filename, but we have placed it in globals, so we don't need to type it again.

so allow me to translate this sprite into a language you might find easier to understand. these 2 lines of code tell foo_looks to do the following:

- look inside the graphics file and start extracting from x:285 y:12 (srcoff)
- place this button in my look at the coordinates x:110 y:73(rect x & y)
- draw the button so that it is 31 pixels wide & 24 pixels high (rect width & height)

does this make sense?

the hardest part is locating the x,y coordinates of the buttons. i use Adobe Photoshop, which always gives me the coordinates of the mouse pointer. but most graphic editing software will do the same

here's a task for you:

try adding another sprite, sprite stopbutton

the only help i will give you is the rect: (don't cheat by reading on ahead)

list rect int { 143 73 31 24 }

did you manage it ok??

your srcoff coordinates should be { 317 12 }

so, you should now have the following:

sprite playbutton
list rect int { 110 73 31 24 }
list srcoff int { 285 12 }

sprite stopbutton
list rect int { 143 73 31 24 }
list srcoff int { 317 12 }

ok great, we've placed 2 buttons on the look, but they don't do anything yet.

Wait - they don't even change when i rollover, or press them!! why not?

because they are using the global settings for statemap, which is states.none

now we will discuss the statemap properly...

The Statemap.

the statemap is the method we use to tell foo_looks how we want buttons to react to mouse events such as rollover, press & toggle.

you will now discover why it was so important to keep your button graphics in neat columns.

each button state is given a number, starting from 0, for each state you have drawn in the source graphics file. Lets look at the playbutton in our tutorial graphics as an example:

when we set the global statemap as none, i told you this was the equivalent of writing:

list statemap int { 0 0 0 0 0 0 0 0 0 0 }

i can now tell you that this means 'show state 0 for every event'

and this explains why our buttons don't ever change from state 0

what we really want is to show state 1 for Rollover, and state 2 for Press.

insert this new statemap into your playbutton & stopbutton sprites:

list statemap int { 0 1 2 2 0 0 1 2 2 0 }

and finally, let me explain the order:

list statemap int { normal, rollover, pressed, pressedrollover, disabled, toggled, toggledrollover, toggledpressed, toggledpressedrollover, toggleddisabled }

or in short { N, R, P, PR, D, T, TR, TP, TPR, TD, }

a little confusing at first ... but you'll get used to it i'm sure

Note: we don't need to set the toggled versions because none of our buttons toggle so far, so you could use the following instead:
list statemap int { 0 1 2 2 0 0 0 0 0 0 0 }

your skin should now look something like this:

 

great... so the buttons now respond to rollover & press events.... but they still don't do anything!

don't worry. you may have completed this tutorial, but the guide is far from over.

from now on, you will be in control of what you decide to learn.

for this reason, i have decided to teach you the features in mini how-to guides. therefore, you can decide exactly which guides you choose to read, and in which order.

The first of these guides is numbered #001, and is entitled 'attaching scripts & inline functions to sprites'

head over to the how-to section and start reading it.

after reading the guide, you can continue your training any way you like, either in the numeric order, or whichever ones interest you.
we plan to write guides for many of the extensive list of features for the plugin, so check the site often.

have fun,

 

tk32

 

Part 3: LUA scripts