Moai Tutorial Part 5: Mi GUI es tu GUI… or how to make buttons and stuff

11. September 2012

 

In this recipe, we are going to show how to create a GUI using Moai.  This functionality isn’t actually built directly into Moai, it is provided by a third party library, so we have to do a bit of setup first.

 

There are a few ways you can get moaigui.  First, it is already included in your moai install in the samples directory.  In the samples folder, look for a contrib folder, in my case it was located at C:\moai-sdk\samples\contrib.  Select the folder moaigui and copy it into your project directory.  Otherwise you can get the newest version using github. To do so ( assuming you have git installed and configured ), open a console window, change directory to your project and type:

git clone https://code.google.com/p/moaigui/

 

This will clone the newest source of the moaigui as a subdirectory in your project.  Alright, that done, on with the code:

 

screenWidth = MOAIEnvironment.screenWidth
screenHeight = MOAIEnvironment.screenHeight
if screenWidth == nil then screenWidth =1280 end
if screenHeight == nil then screenHeight = 800 end

MOAISim.openWindow("Window",screenWidth,screenHeight)

viewport = MOAIViewport.new()
viewport:setSize(screenWidth,screenHeight)
viewport:setScale(screenWidth,screenHeight)

package.path = './moaigui/?.lua;' .. package.path
require "gui/support/class"
local moaigui = require "gui/gui"
local resources = require "gui/support/resources"
local filesystem = require "gui/support/filesystem"
local inputconstants = require "gui/support/inputconstants"
local layermgr = require "layermgr"


local gui = moaigui.GUI(screenWidth,screenHeight)


gui:addToResourcePath(filesystem.pathJoin("moaigui/resources", "fonts"))
gui:addToResourcePath(filesystem.pathJoin("moaigui/resources", "gui"))
gui:addToResourcePath(filesystem.pathJoin("moaigui/resources", "media"))
gui:addToResourcePath(filesystem.pathJoin("moaigui/resources", "themes"))

layermgr.addLayer("gui",99999, gui:layer())
gui:setTheme("basetheme.lua")
gui:setCurrTextStyle("default")

function onButtonClick(event,data)
    label1:setText("You clicked the button")


end



function onLessProgressButtonClick(event,data)
    local curProgress = progress:getProgress()
    if(curProgress > 0) then
        progress:setProgress(curProgress-10)
    end

end

function onMoreProgressButtonClick(event,data)
    local curProgress = progress:getProgress()
    if(curProgress < 100) then
        progress:setProgress(curProgress+10)
    end
end


button = gui:createButton()
button:setPos(0,0)
button:setDim(100,25)
button:setText("This is a button")
button:registerEventHandler(button.EVENT_BUTTON_CLICK,nil,onButtonClick)
button:registerEventHandler(button.EVENT_TOUCH_ENTERS,nil,onButtonClick)

progress = gui:createProgressBar()
progress:setPos(0,25)
progress:setDim(100,25)
progress:setText("This is a progress bar")

button2 = gui:createButton()
button2:setPos(0,50)
button2:setDim(49,25)
button2:setText("Less Progress")
button2:registerEventHandler(button.EVENT_BUTTON_CLICK,nil,onLessProgressButtonClick)
button2:registerEventHandler(button.EVENT_TOUCH_ENTERS,nil,onLessProgressButtonClick)


button3 = gui:createButton()
button3:setPos(51,50)
button3:setDim(49,25)
button3:setText("More Progress")
button3:registerEventHandler(button.EVENT_BUTTON_CLICK,nil,onMoreProgressButtonClick)
button3:registerEventHandler(button.EVENT_TOUCH_ENTERS,nil,onMoreProgressButtonClick)
button3:registerEventHandler(button.EVENT_TOUCH_TAP,nil,onMoreProgressButtonClick)

label1 = gui:createLabel()
label1:setPos(0,75)
label1:setDim(100,25)
label1:setText("Click the top button")
label1:setTextAlignment(label1.TEXT_ALIGN_CENTER)

function onPointerEvent(x, y)
    gui:injectMouseMove(x, y)
end

function onMouseLeftEvent(down)
    if(down) then
        gui:injectMouseButtonDown(inputconstants.LEFT_MOUSE_BUTTON)
    else
        gui:injectMouseButtonUp(inputconstants.LEFT_MOUSE_BUTTON)
    end
end

function onTouchEvent(eventType,idx,x,y,tapCount)
    --gui:injectTouch(eventType,idx,x,y,tapCount)
    onPointerEvent(x, y)
        if (MOAITouchSensor.TOUCH_DOWN == eventType) then
                onMouseLeftEvent(true)
        elseif (MOAITouchSensor.TOUCH_UP == eventType) then
                onMouseLeftEvent(false)
        end
end


if MOAIInputMgr.device.pointer then
    MOAIInputMgr.device.pointer:setCallback(onPointerEvent)
    MOAIInputMgr.device.mouseLeft:setCallback(onMouseLeftEvent)
else
    MOAIInputMgr.device.touch:setCallback(onTouchEvent)
end

Run the code and you will see:

image

 

Buttons and bars and labels, oh my!

 

The top 10 lines are pretty much unchanged from earlier tutorials, except I switched to the native resolution of my device, since I am doing a lot more on device testing lately.  Obviously you can change screenWidth and screenHeight to match your preferred settings.  The new code starts at:

 

package.path = './moaigui/?.lua;' .. package.path
require "gui/support/class"
local moaigui = require "gui/gui"
local resources = require "gui/support/resources"
local filesystem = require "gui/support/filesystem"
local inputconstants = require "gui/support/inputconstants"
local layermgr = require "layermgr"

The first line looks a bit daunting, but it is important to realize that Lua doesn’t really understand file directories the way you or I do.  It uses a variable called package.path to determine where to look for source files.  We want to add the moaigui subdirectory, which is what this line does, we append that folder and tell it to include all .lua files within in the path.  We then have a series of require statements for importing various required parts of the moaigui library.  We don’t have to add paths to these values, as moaigui takes care of that for us.

 

local gui = moaigui.GUI(screenWidth,screenHeight)

Create a moaigui GUI object named gui, by passing in the screens width and height.

 

gui:addToResourcePath(filesystem.pathJoin("moaigui/resources", "fonts"))
gui:addToResourcePath(filesystem.pathJoin("moaigui/resources", "gui"))
gui:addToResourcePath(filesystem.pathJoin("moaigui/resources", "media"))
gui:addToResourcePath(filesystem.pathJoin("moaigui/resources", "themes"))

These are a number of essential files for moaigui, these lines make them all available in the resource path. 

 

layermgr.addLayer("gui",99999, gui:layer())
gui:setTheme("basetheme.lua")
gui:setCurrTextStyle("default")

Add a layer to the moaigui layer manager for the gui layer.  It needs to be the topmost layer so it will receive first crack at all the input coming in.  This is why we set it at layer 99999.  Just don’t go creating a layer 100000 now, ok?  We then load a default theme ( we will look at this in a second, but don’t worry, someone else has already done the work for us ) named basetheme.lua, and set the text style to default… if you don’t do this, text wont show up, so do it.

 

function onButtonClick(event,data)
    label1:setText("You clicked the button")
end

function onLessProgressButtonClick(event,data)
    local curProgress = progress:getProgress()
    if(curProgress > 0) then
        progress:setProgress(curProgress-10)
    end
end

function onMoreProgressButtonClick(event,data)
    local curProgress = progress:getProgress()
    if(curProgress < 100) then
        progress:setProgress(curProgress+10)
    end
end

Here we create 3 button click/touch handlers, one for each button we are going to create.  The first simply updates the text on a label, while the next two advance or decrease the progress status on a progress bar by 10%.

 

button = gui:createButton()
button:setPos(0,0)
button:setDim(100,25)
button:setText("This is a button")
button:registerEventHandler(button.EVENT_BUTTON_CLICK,nil,onButtonClick)

progress = gui:createProgressBar()
progress:setPos(0,25)
progress:setDim(100,25)
progress:setText("This is a progress bar")

button2 = gui:createButton()
button2:setPos(0,50)
button2:setDim(49,25)
button2:setText("Less Progress")
button2:registerEventHandler(button.EVENT_BUTTON_CLICK,nil,onLessProgressButtonClick)


button3 = gui:createButton()
button3:setPos(51,50)
button3:setDim(49,25)
button3:setText("More Progress")
button3:registerEventHandler(button.EVENT_BUTTON_CLICK,nil,onMoreProgressButtonClick)

Speaking of buttons and progress bars, here we create 3 buttons and a progress bar.  There are a few things to note about moaigui.  First off, position is all done by percentages ( which I personally love!  so easy ).  So saying setDim(100,25), sets the dimensions to 100% width and 25% height.  Nice easy way to scale your UI to the device you are running on.  For each of the buttons, we register the click function we just declared in the call registerEventHandler().

 

label1 = gui:createLabel()
label1:setPos(0,75)
label1:setDim(100,25)
label1:setText("Click the top button")
label1:setTextAlignment(label1.TEXT_ALIGN_CENTER)

Oh, and we create a label too, that will update when the user clicks the topmost button.  Using setTextAlignment, we set the text centered.

 

function onPointerEvent(x, y)
    gui:injectMouseMove(x, y)
end

function onMouseLeftEvent(down)
    if(down) then
        gui:injectMouseButtonDown(inputconstants.LEFT_MOUSE_BUTTON)
    else
        gui:injectMouseButtonUp(inputconstants.LEFT_MOUSE_BUTTON)
    end
end

You need to feed UI events in to moaigui, so when the user hits a key, moves the mouse or taps the screen, if you want moaigui to know about it, you need to tell it.  This is done with a series of inject calls, like injectMouseMove() when the user moves the mouse.  If you don’t wire events up as moaigui is concerned, they didn’t happen.  For example, with the current setup, you can type till the cows come home, and your gui will be completely oblivious.

 

function onTouchEvent(eventType,idx,x,y,tapCount)
    --gui:injectTouch(eventType,idx,x,y,tapCount)
    onPointerEvent(x, y)
        if (MOAITouchSensor.TOUCH_DOWN == eventType) then
                onMouseLeftEvent(true)
        elseif (MOAITouchSensor.TOUCH_UP == eventType) then
                onMouseLeftEvent(false)
        end
end

Currently touch support in moaigui is a bit… in development.  So for now we emulate the user using a mouse.  This means when the user touches the screen, we create a mouse pointer event, when the user touches the screen, we create a left click event and when the user releases the screen, be set the left mouse button status to up.  I also got it working by making a few small changes to the moagui code itself, but unless someone specifically requests it, I wont go into them here.

 

if MOAIInputMgr.device.pointer then
    MOAIInputMgr.device.pointer:setCallback(onPointerEvent)
    MOAIInputMgr.device.mouseLeft:setCallback(onMouseLeftEvent)
else
    MOAIInputMgr.device.touch:setCallback(onTouchEvent)
end

Finally, we wire up a callback for each io event we want moai to tell us about.

 

One other thing I mentioned earlier was the theme file basetheme.lua.  This file is located in the /moaigui/resources/themes/ directory, and looks like this:

data = {
    textstyles = {
        default = {
            font = "arial-rounded.TTF",
            size = 44,
        },
        listselected = {
            font = "arial-rounded.TTF",
            size = 14,
            color = {0, 0, 0, 1}
        },
        listunselected = {
            font = "arial-rounded.TTF",
            size = 14,
            color = {1, 1, 1, 1}
        }
    },
    label = {

    },
    button = {
        normal = "button normal center.png",
        pushed = "button pushed center.png",
        hover = "button hover center.png",
    },
    checkbox = {
        normal = "check box.png",
        hover = "check box.png",
        pushed = "check box.png",
        selected = "check box select.png",
    },
    radiobutton = {
        normal = "radio button.png",
        hover = "radio button.png",
        pushed = "radio button.png",
        selected = "radio button select.png",
    },
    editbox = {
        normal = "edit box center.png",
    },
    vertscrollbar = {
        normal = "vert scroll center.png",
        scrollernormal = "vert scroller.png",
        scrollerhover = "vert scroller.png",
    },
    horzscrollbar = {
        normal = "horz scroll center.png",
        scrollernormal = "horz scroller.png",
        scrollerhover = "horz scroller.png",
    },
    vertslider = {
        normal = "vert slider center.png",
        slidernormal = "vert slider.png",
        sliderhover = "vert slider.png",
    },
    horzslider = {
        normal = "horz slider center.png",
        slidernormal = "horz slider.png",
        sliderhover = "horz slider.png",
    },
    progressbar = {
        normal = "progress bar center.png",
        bar = "progress bar.png",
        mask = "progress bar mask.png",
    },
    textbox = {
    
    },
    widgetlist = {
        selected = "listselected",
        unselected = "listunselected",
    },
    cursors = {
        default = {

        },
    },
}

return data

You can easily make small changes to your UI look and feel by changing this file.  As you can see, I vastly increased the default font size to improve visibility on my phone.  You can also change the button images, and other UI elements, by altering their images in the /moaigui/resources/gui directory, while additional or different fonts should go in the moaigui/resources/fonts folder.

 

In the next exciting tutorial we will put our new found GUIness to work.

 

Now you know, and knowing… is half the battle. Or was that a quarter?  No, no, definitely half.

 

Programming , ,







blog comments powered by Disqus