Ruby by Example: Creating a Simple Button

Ruby by Example: Creating a Simple Button

3 Comments

In this tutorial I will show you how to create a simple button in Ruby. Flowstone comes with a button already, this tutorial is just meant to help you to develop your our GUI elements in Ruby, creating a button is a very simple first step.

I will not go into any real depth about the Ruby language here, this tutorial is just meant to be a stepping stone. As such it is very focused on Flowstone specific elements in Ruby. Check out tulamides great Ruby Stripped series for a more in depth look at Ruby.

Lets get started…

Firstly we need to set up the schematic, so make sure you have a new schematic open and press “m” this will give you a module for the button. Now enter the module and press “r” followed by “g” and then “e” now you have the primitives needed to get started. Hook the wireless link to the output of the MGUI primitive. On the Ruby component right click the string input and change it to a view input (yellow ‘V’) now connect this to the MGUI.

componentsCrop

Extra inputs

Next we can set any extra inputs or outputs.  We will need a string input for the buttons label so add another input by dragging the handle and change this to String type.  Name this “label” (right click, and “N”). It is a good idea to set the main colours for the GUI from outside the Ruby component and pass them in via the colour connector type. Colours can be set via code but you will need to look up the RGB values (plenty of websites for this). Using a colour component is much easier as you can easily view the colour from within Flowstone before choosing it.

So lets add two more inputs and set the connectors to the colour data type. Name one “gradient1″ and the other “gradient2″. These will be for our gradient background.

We now just need one output of trigger type that tells us the button was pressed. You should already have an output that is set to string , just change that to the trigger type. Don’t worry about setting a name for this output.

Now just connect a string component and two colour components, set the label to whatever you like. I am using “My Button”, the colour components should be the same colour but the bottom one should be darker.  So now your schematic should look something like this…

Ruby

Now we are ready for some Ruby…

Methods

The Ruby component has certain built in methods that help us to perform certain actions or receive certain information. Methods such as “draw” and “mouseLDown”, these methods should be pretty self explanatory, we use “draw” to draw to the screen and “mouseLDown” gives us the x and y coordinates of a left mouse click. Check the  Flowstone User Guide for available methods.

When creating our own Ruby components we have to first figure out which methods we will need, we don’t always need them all. There is a method called “init” which is used to create and initialize any objects/variables we may need throughout the code, this is called on load. We will also need the “draw” method.

def init()

end

def draw(v)

end

The parentheses () are optional in Ruby but I prefer to always use them, it often results in clearer code and is less error prone.

Another important method we will need is “isInMousePoint”, this tells Flowstone that our Ruby component wants to enable mouse actions (click, drag ect). This is very important! If you forget to define this method but implement other mouse methods such as “mouseLDown” they will not work. So if you need mouse events always remember “isInMousePoint”. The method returns either true or false. You can assign the mouse area to a specific region using this method, you just check if the x and y values are within an area and return true or else return false. In this simple button example we will not worry about areas and instead just return true, now the whole of the button area will be clickable.

def isInMousePoint(x,y)
    return true
end

The return statement is also optional in Ruby! Again it is just my personal preference to include it, I like to be as explicit as possible.

We will also need the event method. The event method is called whenever an input changes such as the label. All we need to do in this example is trigger a redraw when an input changes. There are 3 inputs the first “i” is the index of the input that has changed. The second “v” is the value of that input. “t” is the time that the event arrived. But we do not need to use these for this simple button.

def event(i,v,t)
    redraw
end

Now we just need to define our mouse handling methods. These are “mouseLDown” and “mouseLUp”.

def mouseLDown(x,y)

end

def mouseLUp(x,y)

end

These shouldn’t need much explaining, “mouseLDown” is called when the left mouse button is pressed, “mouseLUp” is called when it is released.

Filling out the methods

State

Firstly we should think about what state our component can have. A button can only be in two possible states, either pressed or not pressed. The prefect way to represent such a state is with a boolean value which can only be true or false. In Ruby we do not explicitly declare a variables type like in many other languages, for instance in Java we would write…

boolean isPressed = false;

In Ruby we can write…

@isPressed = false

Notice we didn’t write”boolean” this is because Ruby is smart enough to figure out the type by the value we assign it. The @ means the variable is an instance variable, meaning it will be available throughout our code and not just inside the method that declares it.

The init method

The init method as mentioned previously is used to initialise and create any variables/objects we will need up front. This method is called once on load. We will create a Font and a StringFormat object for drawing our label, as well as initialising our @isPressed boolean to false. We will also create a pen for our outline and a brush for the font.

def init
    @isPressed =false
   #Label
   @font = Font.new('Arial', 1.2, 'bold')
   @sf = StringFormat.new
   @sf.setLineAlignment("center")
   @sf.setAlignment("center")
   @outline = Pen.new(0.2,Color.new(0))
   @fontBrush = Brush.new(Color.new(255))
end

Font and StringFormat are Flowstone specific objects, they are described in the Flowstone user guide on page 163. We have set the font to bold “Arial” with a size 1.2 grid squares. Alignment is center of the button.

The draw method

This method is called after the init method and then whenever there is a redraw. Redraws can happen often so it is a good idea to make this method as optimized as you can. We have actually already performed an optimization for the draw method by creating our Font and StringFormat objects in the init method. Now they won’t be unnecessarily created again with every redraw. But as a beginner do not worry too much about optimization, but if you know that you need an object such as Font and it will never change then create it in the init method.

So here is our draw method for this button…

def draw(v)
   
    buttonArea = [0.5, 0.5, v.width-1, v.height-1]
    
    gradient1 = @gradient1
    
    if(@isPressed)
       gradient1 = @gradient2
    end
    
    #Draw Background
    fill = LinearGradientBrush.new(buttonArea ,gradient1,@gradient2, 90,false)
    v.drawRoundRect(fill, buttonArea, 1)
   
    #Draw outline
    v.drawRoundRect(@outline, buttonArea, 1)
    
    #Draw Label
    v.drawString(@label, @font, @sf, buttonArea, @fontBrush)
end

First of all we create an area called buttonArea. Notice that it doesn’t have the @ sign before it, this means it is a local variable and is not accessible outside of this draw method. ButtonArea is just an array containing x, y, width and height values. A button looks better with a margin around it, hence the offset 0f 0.5 for x and y and the subtraction of 1 from both the width and height. Now we will have a space around the button.

Next we create another local variable called gradient1 and assign it @gradient1, this is one of the colours from our inputs. Even though these both have the same name they are not the same because of the @ sign. Always name your inputs and then access them anywhere in your code with the @ sign. It is possible to access inputs without naming them, but that is a bad practise so I will not bother showing that.

Now we check if the button is pressed and change the value of gradient1 to @gradient2, we do this so that the button changes to being one colour while being pressed. This gives visual feedback that the button is pressed.

Now we can draw the different elements of our button. First always draw your background. Here we are using a LinearGradientBrush, this is perfect for a button. This makes the button look less flat and more like a real button. Check the Flowstone User Guide page 173 to learn about LinearGradientBrush.

The methods drawRoundRect, and drawString are explained in the user guide from page 158 onwards.

The “v” is our view which is passed to the draw method. We can perform many things by calling methods on this v object as seen already in this example.

 

The mouseLDown method

Here we are simple going to change the state of the button, perform a redraw and then output a trigger.

def mouseLDown(x,y)
    @isPressed =true
    redraw
    
    #Now send a trigger to the output
    output nil
end

The redraw method can also take an area, but for this button redrawing the whole area is fine. In some cases for efficiency you will want to pass a specific area to redraw for example “redraw(area)”.

The only other interesting part is the output method. Usually we pass it the name of the output such as output “nameOfOutput” , nil . But in this case the button only needs one output so there is no benefit to giving it a name. When we don’t supply a name then the value will always be passed to the first output. nil literally means “no value” or “empty”, this is perfect for sending a trigger because a trigger is just an event and has no real value.

 

The mouseLUp method

This is also a nice and simple one. All we need to do now when the user releases the mouse is set the state back to “not pressed” and redraw.

def mouseLUp(x,y)
    @isPressed = false
    redraw
end

Simple!

Exercises

Here are a few exercises for you to do to help with learning.

1. Add extra inputs for Font and String Format, use the Font and String Format primitives instead of the objects you created in the init method.

2. Modify the button to be an on/off button. So the button outputs a boolean instead of a trigger and the button stays on until you press it again.

3. Modify the button to use bitmap images for on/off state.

 

That’s all folks!

This tutorial was meant to help those people that are taking there first steps in Ruby or have avoided it until now. I purposely avoided going into detail about Ruby itself, this was meant to help bridge the gap between what you may read about Ruby and how it is used in the context of Flowstone.

I am planning to do more of these tutorial in the future, each one will get slightly more complex. So if there is anything you would like to see a tutorial on then just let me know in the comments and I will give it consideration, thanks!

1 1 0 0 0
Exo

About the author:

I am the founder of Flowstone Guru. I have been using Flowstone since the early days when it was Synthmaker, talking 7-8 years now! I have created lots of things in Flowstone and have a wealth of experience with the software, some of my work you will find on this site in the Downloads section. I'm passionate about programming (Flowstone, Java ,C++,Ruby) there is nothing I like to do more. I just love a challenge and enjoy pushing myself and the tools I use to there limits!

Tags:

3 Comments

  1. father  - June 2, 2015 - 3:26 pm

    Thanks Exo! How about changing the cursor over an area? Is there a mouse over event?

    • Exo
      Exo  - June 3, 2015 - 9:17 pm

      Hey Father, yes there is a specific method to change the mouse cursor… mouseCursor(x,y), in the method just check if the x and y values are within an area and return a number, which represents the mouse cursor. Numbers used for different cursors are in the user guide.
      Example…
      mouseCursor(x,y)
      if(x>5)
      return 2
      else
      return 8
      end
      end

  2. tony1812  - February 10, 2016 - 7:13 am

    Hi I write the code and when I write the line

    v.drawString(@label, @font, @sf, buttonArea, @fontBrush)

    this gave me an error.

Add Comment Register



Leave a comment

You must be logged in to post a comment.

Back to Top