Tutorial: Focus

This tutorial tells you how to use Focus in Quorum

An Introduction to Focuses in Quorum

In this tutorial, we will learn how the Focus works in the Quorum Game Engine. The Focus is an element within the Item class that is used to indicate which Item is currently selected by the user. If an Item does not have the current Focus, it cannot be activated. Typically, the Focus cycles through several Focusable Items. In Quorum, this is done by manually setting the next and previous Focuses for each Item in the cycle. Having a cycle of focusable Items also allows the user to go through all these items with the keyboard.

For this tutorial, we will create several Drawables and allow the user to cycle the Focus through them. When one of the Items receives the Focus, it will change color to green and say which shape it is, and when the Item loses the Focus, it will change color to red and output which shape lost the Focus and which shape gained the Focus. To start, create a new Game Application project.

Creating a Focusable Drawable

Before we can add much to the main, we need to create a new class. We will name this new class "FocusDrawable.quorum." We will need the libraries for Drawable, FocusEvent, Color, and Speech.

The FocusDrawable class will inherit the Drawable class, and we will override the GainedFocus and LostFocus actions to execute some instructions when the Drawable gains the Focus. We will also need a text class variable for the shape’s description and actions to set and get that description. This gives us the following template:

use Libraries.Game.Graphics.Drawable
use Libraries.Interface.Events.FocusEvent
use Libraries.Game.Graphics.Color
use Libraries.Sound.Speech

class FocusDrawable is Drawable
    action GainedFocus(FocusEvent event)
    end

    action LostFocus(FocusEvent event)
    end
end

For the GainedFocus action, recall that this class inherits the Drawable class. Thus, the FocusDrawable item itself will be the shape we want to change the color of. Thus, we need to use "me:SetColor(color:Green())." Additionally, the Speech class will be used to have the program say the Description of the Drawable aloud. This gives us the following action:

action GainedFocus(FocusEvent event)
    Color color
    me:SetColor(color:Green())

    Speech speech
    speech:Say(GetDescription())
end

Now we can move on to LostFocus, the final action. This action will need to set itself to the color Red, and output the internal Description of the Item that just lost the Focus. This gives us the following code:

action LostFocus(FocusEvent event)
    Color color
    me:SetColor(color:Red())

    output "The " + GetDescription() + " has lost the Focus."
end

Cycles Using Focus

We now have an Item that does something when it gains or loses the Focus, but that alone does not fully utilize the focus. To make use of having focus we can create a cycle that the focus goes through by setting each each Item’s NextFocus and PreviousFocus Item.

Now that we’ve created the FocusDrawable class, we can return to the main class. This class will need the Color library, so we must add the appropriate use statement. Since we want the cycle of shapes to be usable as soon as the game begins, we will write our new code into the CreateGame class. To start, we will create a Color variable and six FocusDrawable objects. These objects will be two rectangles, two circles, and two triangles. The following are our declarations:

Color color
FocusDrawable rectangle1
FocusDrawable circle1
FocusDrawable triangle1
FocusDrawable rectangle2
FocusDrawable circle2
FocusDrawable triangle2

Now we will load each shape. Since the FocusDrawable class inherits the Drawable class, we can use the Drawable class’s LoadFilledRectangle, LoadFilledCircle, and LoadFilledTriangle actions. This adds the following lines of code:

rectangle1:LoadFilledRectangle(50, 50, color:Red())
circle1:LoadFilledCircle(25, color:Red())
triangle1:LoadFilledTriangle(0, 0, 25, 50, 50, 0, color:Red())
rectangle2:LoadFilledRectangle(50, 50, color:Red())
circle2:LoadFilledCircle(25, color:Red())
triangle2:LoadFilledTriangle(0, 0, 25, 50, 50, 0, color:Red())

To make the shapes match the cycle we will make later, we will arrange them in a circle on the game window. This gives the following lines:

rectangle1:SetPosition(375, 500)
circle1:SetPosition(600, 375)
triangle1:SetPosition(600, 175)
rectangle2:SetPosition(375, 50)
circle2:SetPosition(150, 175)
triangle2:SetPosition(150, 375)

Next, we need to call SetDescription for each FocusDrawable to give it an appropriate description. Our calls are as follows:

rectangle1:SetShapeDescription("First Rectangle")
circle1:SetShapeDescription("First Circle")
triangle1:SetShapeDescription("First Triangle")
rectangle2:SetShapeDescription("Second Rectangle")
circle2:SetShapeDescription("Second Circle")
triangle2:SetShapeDescription("Second Triangle")

Before we add each Item to the Focus cycle, we need to make them Focusable objects. Some Items are Focusable by default, such as TextBoxes and Trees, but Drawables are not, so we must set them manually. This is done by calling the SetFocusable action with a boolean parameter of true. This adds the following lines:

rectangle1:SetFocusable(true)
circle1:SetFocusable(true)
triangle1:SetFocusable(true)
rectangle2:SetFocusable(true)
circle2:SetFocusable(true)
triangle2:SetFocusable(true)

Now that each Item is Focusable, we need to manually set the next and previous Focus for each Item. We will set the next Focus from rectangle1 to circle1, from circle1 to triangle1, then to rectangle2, circle2, triangle2, and finally have triangle2 go to rectangle1, forming a cycle, while the previous Focuses will go in reverse order. This gives us the following code:

rectangle1:SetNextFocus(circle1)
rectangle1:SetPreviousFocus(triangle2)
circle1:SetNextFocus(triangle1)
circle1:SetPreviousFocus(rectangle1)
triangle1:SetNextFocus(rectangle2)
triangle1:SetPreviousFocus(circle1)
rectangle2:SetNextFocus(circle2)
rectangle2:SetPreviousFocus(triangle1)
circle2:SetNextFocus(triangle2)
circle2:SetPreviousFocus(rectangle2)
triangle2:SetNextFocus(rectangle1)
triangle2:SetPreviousFocus(circle2)

With the next and previous Focus set for each Item, now we need to add them to the game. This is done with the following lines:

Add(rectangle1)
Add(circle1)
Add(triangle1)
Add(rectangle2)
Add(circle2)
Add(triangle2)

Finally, we must start the Focus on one of our Items, otherwise we cannot begin the cycle. We will begin with rectangle1. This is done simply by calling the Focus action, giving us the following line:

rectangle1:Focus()

Now when we run the program, the shapes are arranged in a circular pattern and can be cycled through, saying aloud the current Focus and turning it Green, and outputting when an Item loses the Focus and turning it back to Red. To move forwards to the next Focus, press Tab, and to move backwards to the previous Focus, press Shift + Tab. Alternatively, the shapes can be clicked on with the mouse to change the Focus.

GetNextFocus and GetPreviousFocus

There are two additional actions relating to the Focus that are included in the Item class. These are GetNextFocus and GetPreviousFocus, both of which return an Item. To demonstrate these, we will modify the GainedFocus action in the FocusDrawable class from the program we created in the previous section.

To start, delete the line of "speech:Say(GetDescription())." Rather than have the program say which shape we are on, we will alter it to prompt the user to move to the next or previous shape.

First, we need to create a new FocusDrawable object, nextDrawable, and assign it to the value returned from calling the current Item’s GetNextFocus action. However, this action returns an Item, which is fine since the actions we are using are part of the Item class. Then we can use the GetDescription action of the obtained Item to get its Description. This gives us the following lines of code:

Item nextDrawable = me:GetNextFocus()
text nextDescription = nextDrawable:GetDescription()

Next, we will do the same to get the description of the previous FocusDrawable, only changing the variable names and calling GetPreviousFocus instead of GetNextFocus. This is done with the following lines:

Item previousDrawable = me:GetPreviousFocus()
text previousDescription = previousDrawable:GetDescription()

Finally, we will use the Speech class to prompt the user aloud and we will also output the statement. We accomplish this by adding the following line:

speech:Say("Tab to reach the " + nextDescription + ", Shift Tab to reach the " + previousDescription)
output "Tab to reach the " + nextDescription + ", Shift Tab to reach the " + previousDescription

Now, when we run the program, it will verbally tell the user and output the description of both the next and previous Focuses when a new Focus is acquired.

Next Tutorial

In the next tutorial, we will discuss Dialogs, which describes how to use Dialogs.