Calling a function within the class through a button

I’m working on my next app, and I’m using a custom menu with my own menu, where there are 4 circles around a central circle and you drag one of the 4 circles to the center and release to trigger the action specified in the init section of the menu class. It is similar to the button class seen in the examples, but works with a deferent ui concept. The question I’m getting to is what would i set the action to in ~~~ menu:init ~~~ for a function located within menu? And is there a specific way to call it (in the button class i just use self.action() )

@Mr_Ninja - assuming the function you want to call is named A, and the menu instance is m, then you simply write m:A()

--# Main
-- Math circles

-- Use this function to perform your initial setup
function setup()
menu = Menu()
    supportedOrientations (LANDSCAPE_ANY)
    displayMode (FULLSCREEN)
    reinit = true
end

-- This function gets called once every frame
    function draw()
 if reinit == true then
        menu:init()
    end
    reinit = false
        menu:draw()
end

function touched (touch)
    plus:touched (touch)
    minus:touched (touch)
    division:touched (touch)
    multiplication:touched (touch)
end



--# Menu
Menu = class()

function Menu:init()
    plus = Button(true, "+" , 125, HEIGHT/2, HEIGHT/2 + 200, WIDTH/2, Menu:Game())
    minus = Button(true, "-", 125, HEIGHT/2, HEIGHT/2 - 200, WIDTH/2)
    division = Button(false, "/", 125, WIDTH/2, HEIGHT/2, WIDTH/2 - 200)
    multiplication = Button(false, "x", 125, WIDTH/2, HEIGHT/2, WIDTH/2 +200)

    gameadd = GameAdd()
    
end
function Menu:Game()
    print ("received #yay")
    background(0, 233, 255, 255)
    text ("hi", WIDTH-20, HEIGHT-20)
end
function Menu:draw()
      background(232, 12, 7, 255)
         fill(0, 204, 255, 255)
     ellipse (WIDTH/2, HEIGHT/2, 125)
      fill(0, 0, 0, 255)
    font("Copperplate-Light")
    fontSize (30)
    text("START", WIDTH/2, HEIGHT/2)
    fill(62, 255, 0, 255)
    fontSize(75)
    text ("Math Circles", WIDTH/2, HEIGHT -50)

    plus:draw()
    plus.setpos = vec2 (WIDTH/2, HEIGHT/2 + 200)
    minus:draw()
    minus.setpos = vec2 (WIDTH/2, HEIGHT/2 -200)
    division:draw()
    division.setpos = vec2 (WIDTH/2 -200, HEIGHT/2)
    multiplication:draw()
    multiplication.setpos = vec2 (WIDTH/2 + 200, HEIGHT/2 )
  
    
end

function Menu:touched(touch)
    -- Codea does not automatically call this method
end

--# Button
Button = class()

function Button:init(vertical, symbol, radius, trigger, defaulty, defaultx, action)
    self.vertical = vertical
    self.symbol = symbol
    self.size = radius
    self.trigger = trigger
    self.setpos = vec2 (defaultx, defaulty)
    self.position = vec2 (defaultx, defaulty)
    self.action = action
end

function Button:draw() 
self.touchpos = vec2 (CurrentTouch.x, CurrentTouch.y)
    self.touchdist = self.touchpos:dist(self.position)
       fill(50, 222, 10, 255)
    ellipse (self.position.x, self.position.y, self.size)
    fill(0, 0, 0, 255)
          font("Copperplate-Light")
    fontSize (50)
     text (self.symbol, self.position.x, self.position.y) 


    
     if self.touch == true then
        if self.vertical == true then
            if self.touchdist < self.size/1.5 then
                if self.trigger > self.setpos.y then 
                    self.position.y =math.max (CurrentTouch.y, self.setpos.y)
                    self.position.y =math.min(self.trigger, self.position.y)
                else
                self.position.y =math.min (CurrentTouch.y, self.setpos.y)
                self.position.y= math.max (self.trigger, self.position.y)
                end
                end
            else
            if self.touchdist < self.size/1.5 then
             if self.trigger > self.setpos.x then
                   self.position.x =math.max ( CurrentTouch.x, self.setpos.x)
                    self.position.x = math.min(self.trigger, self.position.x)
                    else
                    self.position.x = math.min (CurrentTouch.x, self.setpos.x)
                    self.position.x = math.max(self.trigger, self.position.x)
                    end
                
            end
        end
    end
    if self.touch == false then
        if self.position.y == self.trigger then
        self.action()
        else
    self.position = self.setpos
            end
        end
end

function Button:touched(touch)
    if touch.state == BEGAN then
        self.touch = true
    elseif touch.state == ENDED then
        self.touch = false
        end
end

--# GameAdd
GameAdd = class()

function GameAdd:init(x)
    -- you can accept and set parameters here
    self.x = x
end

function GameAdd:draw()
    background(255, 158, 0, 255)
end

function GameAdd:touched(touch)
    -- Codea does not automatically call this method
end

Here’s the code. Keep getting the error “self.action == nil”. I’m sure it’s going to be some stupid error on my part, but I’ve been looking through the code for 2 days and cant spot it.

Mr_Ninja - a tricky one.

First, when providing a callback function, don’t include brackets (they tell Codea to run the function straight away). If you want to include brackets, then you have to wrap the function in another function as shown below.

Second, you can’t include the colon in the callback function like that. Use a full stop, which I think works for you. So instead of Menu:game(), write either of these

function() Menu.Game() end
Menu.Game

…and don’t forget to include them for all four arithmetic signs!

PS if you really need to use a colon in the callback, (ie you are using a specific instance of Menu and need to access it’s self variables) there is a way, but I’ve forgotten it. Someone here will tell you how, if you need it.

@Mr_Ninja - I would recommend replacing this:

function touched (touch)
    plus:touched (touch)
    minus:touched (touch)
    division:touched (touch)
    multiplication:touched (touch)
end

with this:

function touched (touch)
    menu:touched (touch)
end

and then replacing this:

function Menu:touched(touch)
    -- Codea does not automatically call this method
end

with this:

function Menu:touched(touch)
    plus:touched (touch)
    minus:touched (touch)
    division:touched (touch)
    multiplication:touched (touch)
end

you might also want to look into making those buttons local to the class, and even putting them into a table, so you can easily add more buttons

Thanks again Ignatz and Jakattack

@Ignatz - if you need to access a certain instance can’t you just use:

Menu.game(instance,...)
or
instance:game(...)

@Coder You can’t pass the instance into a callback unless you do it like this:

tween(..., function()
    instance:callback(...)
    or type.callback(instance, ...)
end)

(Pseudo-code)

@JakAttack, how would I make the buttons local? I add local before i initialize every button, but then I get an error (button name here) = nil

@Mr_Ninja, I meant local to the class, not local to the function.

what you did is local to the function, so when you try to access them outside that function you get an error like the one you said.

local to the class is using self like so:

function Menu:init()
    self.plus = Button(true, "+" , 125, HEIGHT/2, HEIGHT/2 + 200, WIDTH/2, Menu:Game())
    self.minus = Button(true, "-", 125, HEIGHT/2, HEIGHT/2 - 200, WIDTH/2)
    self.division = Button(false, "/", 125, WIDTH/2, HEIGHT/2, WIDTH/2 - 200)
    self.multiplication = Button(false, "x", 125, WIDTH/2, HEIGHT/2, WIDTH/2 +200)

    self.gameadd = GameAdd()
end

if you also want to use a table like I suggested, try this:

function Menu:init()
    self.buttons = {
        Button(true, "+" , 125, HEIGHT/2, HEIGHT/2 + 200, WIDTH/2, Menu:Game()),
        Button(true, "-", 125, HEIGHT/2, HEIGHT/2 - 200, WIDTH/2),
        Button(false, "/", 125, WIDTH/2, HEIGHT/2, WIDTH/2 - 200),
        Button(false, "x", 125, WIDTH/2, HEIGHT/2, WIDTH/2 +200),
    }

    self.gameadd = GameAdd()
end

then in draw:

for bi, button in ipairs(self.buttons) do
    button:draw()
end

then in touched:

for bi, button in ipairs(self.buttons) do
    button:touched(touch)
end

Hope this helps!

Thanks @JakAttack, that clears up a lot between local and self, and I will use the table for the buttons. Thanks!

No problem!

In another class, I am having an issue involving variables, whenever i call a self variable that had previously been initialized i get the error “attempt to call local self, a nil value”

EDIT: it seems to work if i use GameAdd.variable for setting and calling the variable.
(GameAdd is the class name)
DOUBLE EDIT: it seems for one of my instances i was using “.” Not “:” and that was my problem

@Mr_Ninja, ah the notorious . vs : strikes again :slight_smile: good to see you figured it our all on your own.