button class

hey guys, i just saw you all are contributing something to this forum, like the scenemanager or spritely, and i just wanted to contribute something to. i think a button class is something what everyone needs, and i tried to make as much possibilities as i can. i hope it helps you! (usage guide implemented)

button = class()
-- by max tamussino
-- contact: max@visuals.at


function button:init(title,x1,y1,x2,y2,colorPressed,colorNotPressed,image1,image2)
    -- this button class makes it easier to create buttons. the best thing is: it keeps
    -- the touched function short ;)
    -- so here is how to use it:
    -- - the title the title should be a string, and should represent the function of the button,
    --   like: "closeButton"
    -- - x1,y1,x2,y2 are the coordinates
    -- - good to know: the button can either be a rect or a sprite (in spritely or not). 
    --        1) For the rect:
    --        - colorPressed: the color the rect is filled with when the button is pressed.
    --        - colorNotPressed: the color the rect is filled with when the button is not pressed.
    --        - image1: must be set to ""rect"" (string!!).
    --        - image2: must be nil.
    --        - coordinates: for rectMode(CORNERS)
    --        2) For the sprite:
    --               Two possibilities:
    --               a) changing image:
    --                   - this possibility changes the normal image (image1) to image2 if 
    --                     the button is touched.
    --                   - colorPressed and colorNotPressed both must be nil.
    --                   - image1: normal image
    --                   - image2: displayed instead of the normal one when touched.
    --               b) changing tint:
    --                   - this possibility changes the fill color of the sprite if the 1
    --                     button is touched
    --                   - colorPressed: the tint() if the button is not touched
    --                   - colorNotPressed: the tint() if the button is touched
    --                   - image2: must be nil
    --
    --
    -- the button:getInfo() function returns a table with the title ( [1] )and the state( [2] ).
    -- to use this, you can can keep track of your buttonstates like this:
    --
    --
    --
    --
    --    if button:getInfo()[2] == 3 then
    --        if button:getInfo()[1] == "closeButton" then
    --            close()
    --        elseif button:getInfo()[1] == "openMenuButton" then
    --            if not menuShown then
    --                menuShown = true
    --            else
    --                menuShown = false
    --            end
    --        elseif button:getInfo()[1] == "showHelpScreenButton" then
    --            if not helpScreenShown then
    --                helpScreenShown = true
    --            else
    --                helpScreenShown = false
    --            end
    --        end
    --    end
    --
    -- i hope it helps you guys! have fun creating buttons! :)
    
    self.title = title
    self.x1 = x1
    self.y1 = y1
    self.x2 = x2
    self.y2 = y2
    self.cp = colorPressed
    self.cnp = colorNotPressed
    self.image1 = image1
    if not self.cp then
        if not self.cnp then 
            self.image2 = image2
        end
    end
    self.pressed = false
    rectMode(CORNERS)
end

function button:draw()
    if self.state == 1 or self.state == 2 then
        self.pressed = true
    else
        self.pressed = false
    end
    
    if self.image1 == "rect" then
        if self.pressed then
            fill(self.cp)
        else
            fill(self.cnp)
        end
        rect(self.x1,self.y1,self.x2,self.y2)
    elseif self.image2 then
        if self.pressed then
            sprite(self.image2,self.x1,self.y1,self.x2,self.y2)
        else
            sprite(self.image1,self.x1,self.y1,self.x2,self.y2)
        end
    else
        if self.pressed then
            tint(self.cp)
        else
            tint(self.cnp)
        end
        sprite(self.image1,self.x1,self.y1,self.x2,self.y2)
    end
    
    if self.state == 3 then
        if ElapsedTime > a + 0.1 then
            self.state = 0
        end
    end
end

function button:touched()
    t = CurrentTouch
    if t.x > self.x1 and t.y > self.y1 and t.x < self.x2 and t.y < self.y2 then
        if t.state == BEGAN then
            self.state = 1
        elseif t.state == MOVING then
            self.state = 2
        elseif t.state == ENDED then
            self.state = 3
            a = ElapsedTime
        end
    else
        self.state = 0
    end
end

function button:getInfo()
    return {self.title, self.state}
end

maxiking16

Thanks @Maxiking16. Why is there


a = ElapsedTime

in the button:touched() ?

Nice. Thanks. I also like to do small classes like my smooth changing color class I haven’t had a chance to post.

hah, i have one of these too :smiley:

How many lines of code is your color class? Just curiouse.

@Herwig : because it Saves the Time when the Finger leaves the button (then the State is 3), and in the draw, 0.1 seconds later the State gets changed to 0, to prevent the Button from having State 3 everytime.

@Zoyt : Not long, about 50 Lines or something.

@Maxiking16 wouldn’t it be better to put that in the class, rather than in a global variable?

Well, actally… Your right. I’ll change it.

Do you want to enter this in my competition?

Id Love to!

Good.

I needed something else for my button class, so i did below additions. I will share it here so others can use it too.

When you have lots of buttons and if you need to remove/add them along your code, you need a good list for your buttons to quickly add/remove them and also search among them.

First i added a global named “button_list” in my setup as nil.
Then i added these lines into my button class init:

    self.prev = nil
    self.next = button_list
    button_list = self
    if self.next then self.next.prev = self end

self.prev is previous button in the list, self.next is the next one as you can guess.
With this, when a button created, it adds that button to the list.

My main touch function:

    if button_list == nil then return end
    
    ch = button_list
    repeat
        ch:touched()
        if ch:getInfo()[2] == 3 then
            if ch:getInfo()[1] == "CloseButton" then close() end
            if ch:getInfo()[1] == "CloseButton2" then remove_button( close_2 ) end
        end
        ch = ch.next
    until ch == nil

CloseButton and CloseButton2 are just examples.

Here is the piece where i draw buttons in my draw main:

    if button_list ~= nil
    then
        ch = button_list
        repeat
            ch:draw()
            ch = ch.next 
        until ch == nil
    end

Also i have this function to remove a button if needed:

function remove_button(b)
    
    if b == button_list
    then
        if button_list.next then button_list.next.prev = nil end
        button_list = button_list.next
    else
        if b.next 
        then 
            b.next.prev = b.prev
            if b.prev then b.prev.next = b.next end
        else
            if b.prev then b.prev.next = nil end
        end
        
    end
    b = nil    
end

Hope this helps out those who need to improve their button classes. There can be bugs, i didnt have much chance to test, just wanted to share and do my bit of help for the great Codea community.

erm, can i learn how you add piece of code to posts so they show properly ?

   ch=nil

like this ?

I edited the post to do the code. This forum uses PHP Markdown Extra. Code blocks are either indented 4 spaces or “fenced” by a line with ~~~ on it before and after.

Thanks Andrew

Thanks Maxiking, this looks great!

Would you be up for adding a wee bit more instruction for us total newbies? I created my buttons in setup() and I’m calling button:draw() from my draw() function. But how do I integrate with touched()? Do I call button:touched() from my main touched() function?

Could you perhaps add, in your instructions, sample code that would create a simple button and watch for a press? (I know this should be obvious from looking at the code, but I’m still such a beginner that I can’t quite figure it out.)

there is an example for it in his instructions Goatherd,

just call your button’s touched in your main “touched” function like

 my_button:touched()

then check its state anywhere you want, like:

    if button:getInfo()[2] == 3 then
         if button:getInfo()[1] == "closeButton" then
             close()

This button closes your running code.

Thanks Reldonas. I thought that was basically what I was doing, but I’m doing something wrong. It draws the button on the screen, showing the “not touched” color, but the color doesn’t change when I press the button, and the displayed text never changes from “nothing yet”.

Here’s my code (with apologies for the truly ill-advised button name, which didn’t occur to me until I got to the “mybutt:touched” portion…)

--# Main
function setup()
  colp=color(221, 255, 0, 255)
  colnp=color(0, 255, 15, 255)
  mybutt=button("num1",500,500,650,450,colp,colnp,"rect")
  fontSize(48)
  font("AmericanTypewriter-Bold")
  disp="nothing yet"
end

function draw()
  mybutt:draw()

  if mybutt:getInfo()[2]==1 then
    disp="it is 1"
  elseif mybutt:getInfo()[2]==2 then
    disp="it is 2"
  elseif mybutt:getInfo()[2]==3 then
    disp="it is 3"
  end

  text(disp,WIDTH/2,HEIGHT/2)

end

function touched(touch)
  mybutt:touched()
end

-- Maxiking's button code follows here

Thanks for any pointers on whatever probably-terribly-embarrassing mistake I’m making.

Your code is ok :slight_smile: just your coordinates are wrong :slight_smile: i mean at least for Maxiking’s code. x1 and x2 should be lower left corner of rect, x2 and y2 upper right. So when you make x2>x1 and y2>y1; it works.

lol and thank you. That’s an even goofier error than what I expected I was making.