Buttons

Hello, I want to make an app with a button. Mine idea was that if you touch the button, you will see something like a dark background ir something in that way. Can you help me guys?

Hey @pepijnpp. Here is @Vegas button class, feel free to use it.

--Much faster version of button class by Vega
button = class()
function button:init(text,location,width,height)
    self.state = "normal"
    self.text = text
    self.textColor = color(255,255,255,192)
    self.location = location
    self.width = width
    self.height = height
    self.visible = true
    self.fontSize = 28
    self.font = "ArialRoundedMTBold"
    self.color1 = color(255, 255, 255, 96)
    self.color2 = color(128,128,128,32)
    self.presscolor1 = color(192, 224, 224, 128)
    self.presscolor2 = color(96, 192, 224, 128)
    self.verts = self:createVerts(self.width, self.height)
    self.myMesh = mesh()
    self.myMesh.vertices = triangulate(self.verts)
    self.vertColor = {}
    self:recolor()
end
function button:setColors(c1,c2,p1,p2)
    self.color1 = c1
    self.color2 = c2
    self.presscolor1 = p1
    self.presscolor2 = p2
    self:recolor()
end
function button:textOptions(fn, sz, col)
    self.font = fn
    self.fontSize = sz
    self.textColor = col
end
function button:draw()
    if self.visible == true then
        pushMatrix()
        translate(self.location.x,self.location.y)
        self.myMesh:draw()
        fill(self.textColor)
        fontSize(self.fontSize)
        font(self.font)
        text(self.text, self.width/2,self.height/2)
        self:drawLines(self.verts)
        popMatrix()
    end
end
function button:touched(touch)
    if self.visible then
        if touch.x >= self.location.x and touch.x <= self.location.x + self.width and touch.y >= self.location.y and touch.y <= self.location.y + self.height then
            if touch.state == BEGAN then
                self.state = "pressing"
                self:recolor()
            elseif touch.state == ENDED then
                if self.state == "pressing" then
                    self.state = "normal"
                    self:recolor()
                    print "pressed."
                end
            end
        else
            self.state = "normal"
            self:recolor()
        end
    end
end
function button:createVerts(w,h)
    local r
    local v = {}
    if w > 100 or h > 100 then
        if w>=h then r = round(h/100) else r = round(w/100) end
    else
        r = 1
    end
    v[1] = vec2(w,6*r)
    v[2] = vec2(w-r,4*r)
    v[3] = vec2(w-2*r,2*r)
    v[4] = vec2(w-4*r,r)
    v[5] = vec2(w-6*r,0)
    v[6] = vec2(6*r,0)
    v[7] = vec2(4*r,r)
    v[8] = vec2(2*r,2*r)
    v[9] = vec2(r,4*r)
    v[10] = vec2(0,6*r)
    v[11] = vec2(0,h-6*r)
    v[12] = vec2(r,h-4*r)
    v[13] = vec2(2*r,h-2*r)
    v[14] = vec2(4*r,h-r)
    v[15] = vec2(6*r,h)
    v[16] = vec2(w-6*r,h)
    v[17] = vec2(w-4*r,h-r)
    v[18] = vec2(w-2*r,h-2*r)
    v[19] = vec2(w-r,h-4*r)
    v[20] = vec2(w,h-6*r)
    return v
end
function button:drawLines(v)
    pushStyle()
    noSmooth()
    strokeWidth(1)
    stroke(0, 0, 0, 192)
    for i=1, #v-1 do
        line(v[i].x,v[i].y,v[i+1].x,v[i+1].y)
    end
    line(v[#v].x,v[#v].y,v[1].x,v[1].y)   
    popStyle()
end
function button:recolor()
    local lt, dk
    if self.state == "normal" then 
        lt = self.color1
        dk = self.color2
    else
        lt = self.presscolor1
        dk = self.presscolor2
    end
    for i=1,3 * #self.verts - 6 do
        if self.myMesh.vertices[i].y > self.height/2 then
            self.vertColor[i] = lt
        else
            self.vertColor[i] = dk
        end
    end
    self.myMesh.colors = self.vertColor
end
function round(v)
    return math.floor(v + 0.5)
end

Enjoy!
P.S. I am working on a button class of my own… :wink: Be excited…

.@pepijnpp - I use the @Vega button class as well, with some minor mods to add a call back function (so you can do something when the button is tapped), you can get a copy here: https://www.dropbox.com/s/crrhfz8kdde4z6v/Button.lua

Or if you want a simpler solution then look at: http://codeatuts.blogspot.com.au/2012/06/tutorial-3-simple-button-class.html

Thank you @Reefwing just grabbed your class cause i needed a callback too: your buttons are beautiful and work fine. I am always amazed how natural and smooth it is to program in lua: i just copied you code, typed a few lines and it works directly as expected, no bug! I’ve done quite some programming in Javascript, that’s a different story: i almost get one error for each line of code and it takes the hell of a time to have something that finally works. I worship LUA!

Hi @Jmv38 - I wish I could take the credit! However the look and feel of the button is courtesy of @Vega and the call back functionality is courtesy of the example code in the Sounds Plus App which comes with Codea. I just stuck them together.

I agree about Lua though, I’m working with Objective C as well and there is a huge difference in complexity and transparency. Even doing something simple in Objective C isn’t simple.

Am new here so maybe this has been answered elsewhere.

Since Codea can use sprites, of what use is it to have all that code just to draw a single button? Wouldn’t it be simpler to use a button image (sprite) and add the code to harness the touched events?

.@bernbout - using sprites is a perfectly valid approach as well. In fact my next tutorial (probably out today) will use that method. It is best suited if you want something that looks a bit fancy. The advantage of the mesh approach is that the class is standalone (you dont need to include the sprite assets) and it takes less memory.

@Reefwing Thanks for clearing that up. I take a more visual approach to programming and prefer to see rather than pure code. I appreciate the usage of the code class where the colours can easily be changed with one line rather than uploading a new sprite.

I know most of Codea is focused on games but there are also Applications and I was wondering how difficult it would be to tap into the IOS native controls and use them instead of programming them completely in Lua. Maybe a bridge-like class that “Implements” native controls? Is that at all feasible?

Why I ask is that I also use Stencyl (stencyl.com) and the guys there have implemented a native text label control for IOS because the other way was eating up GPU cycles and fps. It just is a code block that taps into the native IOS label control.

Your tutorials are excellent though assume prior knowledge in some cases. I am going thru them one by one. I await your latest tutorial. Thanks for your reply.

I bet we can turn that button into a text box with ease…
Have the touch show the keyboard, update the button text.
I’ll play with it.

Hi .@bernbout - My understanding is that most of the rendering for Codea is done using OpenGL. The iOS controls you are speaking off are part of UIKit which isn’t exposed in Codea (AFAIK). I don’t know how hard it would be to implement this but I suspect it is non-trivial or it would have been done already. The quest for the perfect button is ongoing as you will see from the frequency of this topic. @Andrew_Stacey has produced a library which you might want to have a look at.

Glad you are enjoying the tutes and thanks for the feedback - I’m working on the next one at the moment.

Button to TextBox.
Could use some enhancements, like auto size


Textbox = class()

-- Fast Mesh Button Class courtesy of @Vega
-- 3 Aug 2012
--
-- Modified: - Call Back Functionality added
--           - pushStyle() & popStyle() added to draw()
--           - tapped status added
--           - pointInRect() function added
--           - changed vec2 location to x and y (to reduce typing!)
--
-- Version 1.3 (4 Aug 2012)

function Textbox:init(text,x,y,width,height)
    self.state = "normal"
    self.text = text
    self.textColor = color(255,255,255,192)
    self.x = x
    self.y = y
    self.width = width
    self.height = height
    self.visible = true
    self.fontSize = 28
    self.font = "ArialRoundedMTBold"
    self.color1 = color(255, 255, 255, 96)
    self.color2 = color(128,128,128,32)
    self.presscolor1 = color(192, 224, 224, 128)
    self.presscolor2 = color(96, 192, 224, 128)
    self.verts = self:createVerts(self.width, self.height)
    self.myMesh = mesh()
    self.myMesh.vertices = triangulate(self.verts)
    self.vertColor = {}
    self:recolor()
    self.action = nil
    self.tapped = false
end

function Textbox:setColors(c1,c2,p1,p2)
    self.color1 = c1
    self.color2 = c2
    self.presscolor1 = p1
    self.presscolor2 = p2
    self:recolor()
end

function Textbox:textOptions(fn, sz, col)
    self.font = fn
    self.fontSize = sz
    self.textColor = col
end

function Textbox:draw()
    if self.visible == true then
        pushStyle()
        pushMatrix()
        translate(self.x,self.y)
        self.myMesh:draw()
        fill(self.textColor)
        fontSize(self.fontSize)
        font(self.font)
        text(self.text, self.width/2,self.height/2)
        self:drawLines(self.verts)
        popMatrix()
        popStyle()
    end
end

function Textbox:touched(touch)
    self.tapped = false
    if self.visible then  
        if pointInRect(touch.x, touch.y, self.x, self.y, self.width, self.height) then
            if touch.state == BEGAN then
                self.tapped = true
                self.state = "pressing"
                self:recolor()
                self.text =""
                showKeyboard()
            elseif touch.state == ENDED then
                if self.state == "pressing" then
                    self.state = "normal"
                    self.tapped = true
                    self:recolor()
                end
                if self.action then
                    self.action()
                end
            end
        else
            self.state = "normal"
            self:recolor()
        end
    end
end

function Textbox:createVerts(w,h)
    local r
    local v = {}
    if w > 100 or h > 100 then
        if w>=h then r = math.round(h/100) else r = math.round(w/100) end
    else
        r = 1
    end
    v[1] = vec2(w,6*r)
    v[2] = vec2(w-r,4*r)
    v[3] = vec2(w-2*r,2*r)
    v[4] = vec2(w-4*r,r)
    v[5] = vec2(w-6*r,0)
    v[6] = vec2(6*r,0)
    v[7] = vec2(4*r,r)
    v[8] = vec2(2*r,2*r)
    v[9] = vec2(r,4*r)
    v[10] = vec2(0,6*r)
    v[11] = vec2(0,h-6*r)
    v[12] = vec2(r,h-4*r)
    v[13] = vec2(2*r,h-2*r)
    v[14] = vec2(4*r,h-r)
    v[15] = vec2(6*r,h)
    v[16] = vec2(w-6*r,h)
    v[17] = vec2(w-4*r,h-r)
    v[18] = vec2(w-2*r,h-2*r)
    v[19] = vec2(w-r,h-4*r)
    v[20] = vec2(w,h-6*r)
    return v
end

function Textbox:drawLines(v)
    noSmooth()
    strokeWidth(1)
    stroke(0, 0, 0, 192)
    for i=1, #v-1 do
        line(v[i].x,v[i].y,v[i+1].x,v[i+1].y)
    end
    line(v[#v].x,v[#v].y,v[1].x,v[1].y)   
end

function Textbox:recolor()
    local lt, dk
    if self.state == "normal" then 
        lt = self.color1
        dk = self.color2
    else
        lt = self.presscolor1
        dk = self.presscolor2
    end
    for i=1,3 * #self.verts - 6 do
        if self.myMesh.vertices[i].y > self.height/2 then
            self.vertColor[i] = lt
        else
            self.vertColor[i] = dk
        end
    end
    self.myMesh.colors = self.vertColor
end

-- Math Utilities

function math.round(value)
    
    -- math.round function courtesy of Vega.
    
    return math.floor(value + 0.5)
    
end

function Textbox:keyboard(key)

self.text = self.text..key
end
    
function pointInRect(pointX, pointY, x, y, w, h)
    
    -- Returns true if point (pointX, pointY) is within the rectangle
    -- with lower left corner at (x, y) with a width of w and a
    -- height of h.
    --
    -- Reefwing Software (www.reefwing.com.au)
    -- Version 1.0
    
    if pointX >= x and pointX <= x + w and pointY >= y and pointY <= y + h then
        return true
    else
        return false
    end
    
end