Thursday, 17 June 2010

RGB colour mixer application

Couple of days ago I described the Hello World application. Today I'd like to describe something more complicated. It is a colour mixer application. There will be a rectangle which colour will be controlled by three sliders. They will represent Red Green Blue values making up the background. I aim at demonstrating some the important concepts of JavaFX. One of them is binding, second is mouse event and finally the layout manager. The application looks like this:


And that's the way it looks after the sliders have been moved:


and here is the code:


import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.control.Slider;
import javafx.scene.paint.Color;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;
import javafx.scene.input.MouseEvent;

var sceneRef: Scene;
var r: Number = 255;
var g: Number = 255;
var b: Number = 255;

Stage {
    title: "RGB colour mixer"
    width: 270
    height:250
    resizable: false
    scene: sceneRef = Scene {
        fill: Color.LIGHTGRAY
        content: [
            Rectangle {
                 layoutX: 190
                 layoutY: 20
                 width: 50
                 height: 180
                 arcWidth: 20
                 arcHeight: 20
                 fill : bind Color.rgb(r, g, b)
                 onMouseClicked: function(me: MouseEvent): Void {
                     r = g = b = 255;
                 }
            },
          
            VBox {
                spacing: 15
                layoutX: 20
                layoutY: 20
                content: [
                        Text {
                            content: bind "Red value: {r}"
                            font: Font.font("Sans Serif",FontWeight.BOLD, 14)
                        },

                        Slider {
                            min: 0
                            max: 255
                            vertical: false
                            value: bind r with inverse
                        },

                       Text {
                            content: bind "Green value: {g}"
                            font: Font.font("Sans Serif",FontWeight.BOLD, 14)
                        },

                       Slider {
                            min: 0
                            max: 255
                            vertical: false
                            value: bind g with inverse
                        },

                       Text {
                            content: bind "Blue value: {b}"
                            font: Font.font("Sans Serif",FontWeight.BOLD, 14)
                        }
                       Slider {
                            min: 0
                            max: 255
                            vertical: false
                            value: bind b with inverse
                        }
                    ]
            }
        ]
    }
}

Now, that you've studied the source code let me explain it. Like always we start with import directives. There are some new libraries that needed to be imported:


import javafx.scene.control.Slider; // slider
import javafx.scene.layout.VBox;  // VBox layout manager
import javafx.scene.shape.Rectangle; // allows us to draw rectangle
import javafx.scene.input.MouseEvent; // controls mouse events

I think it is straightforward why we need them. In case you need some more information on the classes imported refer to the JavaFX API documentation.
After the import there we have variable declaration. I described two posts ago how we define the variables. As you see we create variable sceneRef of the Scene class. I put it in there so that you can see that the we can create variables of Scene and Stage and then refer to them in the script. The following code: 

scene: sceneRef = Scene {
// code omitted
}

initialises the sceneRef object and assigns it to the scene variable of Stage class. Later in the program we can refer to the Scene using the name sceneRef. It might be the case when we want to change one of the Scene's variables like fill. Without it we would not be able to alter any properties of the Scene.
So far the space to draw has been created. Next step is to place graphical nodes on the surface. As I mentioned in the previous post all nodes are assigned to content variable of Scene. First of them is Rectangle:


Rectangle {
                 layoutX: 190
                 layoutY: 20
                 width: 50
                 height: 180
                 arcWidth: 20
                 arcHeight: 20
                 fill : bind Color.rgb(r, g, b)
                 onMouseClicked: function(me: MouseEvent): Void {
                     r = g = b = 255;
                 }
 }

Rectangle class provides us with facilities to draw a rectangle shape on the screen.As you can see from the object literal above it has number of variables, which allow us to manipulate the shape:

layoutX - x coordinate on the screen, where drawing should begin
layoutY - y coordinate on the screen, where drawing should begin
width - width of the shape
height - height of the shape
arcWidth - width of the rectangle's round corners
arcHeight - height of the rectangle's round corners
fill - colour of the rectangle. In this point we use bind expression. I will explain it below
onMouseClicked - action listener responsible for mouse events

JavaFX has something what is called bind expression. It is very simple but very useful tool. Basically what it does is to glue two variables together.If the variable that is bound to other variable changes the other variable's value is changed as well. In the example code above we've got:

fill : bind Color.rgb(r, g, b)

fill variable is bound to the product of Color.rgb() method. Furthermore, the product depends on r,g and b variables which correspond to values hold by sliders. Every time any of the sliders is moved the corresponding value is updated, this results in the function Color.rgb() being invoked and fill variable updated. At the end what we see is that colour of the rectangle changes. I hope you more or less understand what binding is. If not do not worry as there will be simpler examples in the future (I think).
In the example above we also have action listener, which is assigned to onMouseClicked variable:


onMouseClicked: function(me: MouseEvent): Void {
        r = g = b = 255;
 }

Here we have anonymous function, which return type is Void and which takes parameter me of type MouseEvent. If you have ever worked with UML you should notice that declaration of types of variables are the same in JavaFX as in UML. The mentioned anonymous method is invoked every time we click on the rectangle. The result of its action is to bring the values of the sliders back to 255. Binding is responsible for the sliders to move back to the position where they were when program was first started up and changing the fill colour of the shape to white.

Another object on the scene is VBox:

VBox {
                spacing: 15
                layoutX: 20
                layoutY: 20
                content: [
                            // code omitted
                 ]
}

VBox is on of the JavaFX layout managers. It is responsible for placing elements in vertical manner. As you can guess there is also manager to place nodes on horizontal manner called HBox. There is couple more layout managers, which you can find by studying JavaFX API. Let's have a look on the variables in the example:

spacing - sets the space in pixels between two nodes
layoutX - x coordinate of the beginning of the layout manager
layoutY - y coordinate of the beginning of the layout manager
content - here we place all the nodes that we require to be place in vertical manner

We can place nodes and other layout managers inside the content sequence. As I mentioned in the previous post content is sequence of type Node, which in turn is the grandfather of every class in JavaFX. This allows us to assign any object to the content.

Inside the VBox object we have text and sliders. I will discuss one example of slider as they are all the same. I will skip the Text class as it was already discussed in the Hello World application post.

Here we go, the Slider object:

 Slider {
         min: 0
         max: 255
         vertical: false
         value: bind r with inverse
 }

The variables represent:

min - minimum value of slider's range
max - maximum value of slider's range
vertival - is the slider to be drawn verticality
value - holds value of the slider

As you can see incorporating slider in the program is very simple task. In the above listing you can see that the value of slider is bound with the variable r. As you noticed we added "with inverse" after the variable name. It means that the variables will change if any of them is changed. We can amend the variable r somewhere in the program and at the same time the slider value will be changed and the point on the slider moved to correct position.

There is one more thing I would like to mention. In the Text objects there is the following line:

content: bind "Red value: {r}"

The signs{} are used when we want to display the value of the variable within the string of characters.

That's all for now. I've got some interesting examples coming up next, so stay tuned. Remember that the best way to learn language is to write programs in it. You should try to experiment!! Good luck:)


No comments:

Post a Comment