Callback into class function does not work

I’m working on some UI stuff and on a button radio group. The button is class and I can put a few buttons into a button radio group, so only one button can be toggled. Now I have a callback for the buttons itself to use it as single button. When I add the button to a button group, the callback is exchanged with a callback into the button group. That works so far. But then, when I want to call the callback of my main function, it gives me an error. Even calling functions of the button group class gives me an error. It seems like setting a callback to a class function with self.callback can call the callback, but I’m not really back in the original class (self)?

Some code is worth a thousand words, but I have to post everything now. Its a main file, where some buttons are created. Press one of the toggle buttons on the right to see the error. Then you need the buttonC, ButtonRadioGroupC and FontC.

Main

btn = ButtonC
btnTog = ButtonC
btnGroup = ButtonRadioGroupC

btnRaised = image(1, 1)
btnSunken = image(1, 1)

local function mainBtnCallback(id, state, btn)
    print("mainBtnCallback", id, state)
end

local function mainBtnGroupCallback(id, btn)
    print("groupCallback", id, state)
end

-- Use this function to perform your initial setup
function setup()
    -- Create button graphic:
    btnRaised = createButtonImage(150, 50, 2, color(70, 140, 180), 0.4, true)
    btnSunken = createButtonImage(150, 50, 2, color(70, 140, 180), 0.4, false)
 
    -- Create FontC:
    local fnt = FontC("Futura-MediumItalic", 16, color(255,255,255), color(0,0,0), 1)

    -- Create simple button:
    btn = ButtonC("Btn", 200, 100, 150, 50, btnRaised, btnSunken, "Button One", fnt, 1.1, nil, BTYP_NORMAL, 1, mainBtnCallback)
    -- Createbtoggle button:
    btnTog = ButtonC("BtnTog", 200, 45, 150, 50, btnRaised, btnSunken, "Button Toggle", fnt, 1.0, nil, BTYP_TOGGLE, 1, mainBtnCallback)

    -- Create button group:
    local btntgl1 = ButtonC("Tog1", 375, 100, 150, 50, btnRaised, btnSunken, "Toggle Button A", fnt, 1.0, nil, BTYP_TOGGLE, 1, nil)
    local btntgl2 = ButtonC("Tog2", 375, 45, 150, 50, btnRaised, btnSunken, "Toggle Button B", fnt, 1.0, nil, BTYP_TOGGLE, 1, nil)
    btnGroup = ButtonRadioGroupC(mainBtnGroupCallback)
    btnGroup:addButtons(btntgl1, btntgl2)
    btnGroup:setSelected("Tog1")
end

function touched(touch)
    btn:touched(touch)
    btnTog:touched(touch)
    btnGroup:touched(touch)
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(120, 200, 240)

    -- Do your drawing here
    btn:draw()
    btnTog:draw()
    btnGroup:draw()
end
-- ButtonC by Kilam Malik.
--
-- You also need FontC!

ButtonC = class()

BTYP_NORMAL = 1
BTYP_TOGGLE = 2
BTYP_FOLDOUT = 3
--BTYP_MATRIX = 4

function ButtonC:init(id, x, y, w, h, imgNormal, imgPressed, txt, fnt, clickZoom, foldOutTexts, btyp, direction, callback)
-- id is sent back to the callback function to use one callback for multiple buttons.
-- x, y, w, h is position and size (CORNER).
-- imgNormal and imgPressed are the pictures of the buttons.
-- textMain is the text of the button.
-- foldOutTexts are the texts if the menu is folded out.
-- btyp is the type of the button.
-- direction is the foldout direction of btyp_ foldout.
-- callback is the return function. Gets ID and fold out ID.

    self.id = id
    self.x = x
    self.y = y
    self.w = w
    self.h = h
    self.imgNormal = imgNormal
    self.imgPressed = imgPressed
    self.txt = txt
    self.fnt = fnt
    self.clickZoom = clickZoom
    self.foldOutTexts = foldOutTexts
    self.btyp = btyp
    self.direction = direction
    self.callback = callback

    self.inTouch = false
    self.selected = false
end

function ButtonC:draw()
    pushStyle()
    pushMatrix()

        translate(self.w / 2, self.h / 2) -- I use mode CENTER for easier zoom, but with this translate I can pass corner coordinates.
        local w = self.w
        local h = self.h
        spriteMode(CENTER)
        textMode(CENTER)
        if self.selected or (self.inTouch and self.btyp == BTYP_NORMAL) then
            w = w * self.clickZoom
            h = h * self.clickZoom
            sprite(self.imgPressed, self.x, self.y, w, h)
            pushMatrix()
                translate(self.x, self.y)
                scale(self.clickZoom, self.clickZoom)
                self.fnt:draw(self.txt)
            popMatrix()
        else
            sprite(self.imgNormal, self.x, self.y, w, h)
            pushMatrix()
                translate(self.x, self.y)
                self.fnt:draw(self.txt)
            popMatrix()
        end

    popMatrix()
    popStyle()
end

function ButtonC:hit(x, y)
    if math.abs(self.x+self.w/2 - x) <= (self.w / 2)
       and math.abs(self.y+self.h/2 - y) <= (self.h / 2) then
         return true
    end

    return false
end

function ButtonC:touched(touch)
    if touch.state == BEGAN then    
        if self:hit(touch.x, touch.y) then
            self.inTouch = true
            return true
        end
    elseif touch.state == MOVING and self.inTouch then    
        if not self:hit(touch.x, touch.y) then
            self.inTouch = false
        else
            return true
        end
    elseif touch.state == ENDED then
        if self.inTouch == true then
            if self.btyp == BTYP_TOGGLE then
                self.selected = not self.selected
            end
            if self.callback then
                --print(self.id)
                self.callback(self.id, self.selected, self)
            end
            self.inTouch = false
            return true
        end
    end
    return false
end

-- -------------------------- Helpers ----------------------------

function createButtonImage(x, y, b, col, colFac, raised)
-- x, y = size
-- b = border size
-- col = base color
-- raised = sunken or raised button

    img = image(x, y)
    local colDark = color(col.r * colFac, col.g * colFac, col.b * colFac)
    local colLight = color(col.r / colFac, col.g / colFac, col.b / colFac)

    pushStyle()
    setContext(img)
        noSmooth()
        noStroke()
        rectMode(CORNER)

        -- Bottom right
        if raised then
            fill(colDark)
        else
            fill(colLight)
        end
        rect(0, 0, x, y)

        -- Top left
        if raised then
            fill(colLight)
        else
            fill(colDark)
        end
        rect(0, b, x - b, y - b)

        -- Center
        fill(col)
        rect(b, b, x - 2 * b, y - 2 * b)
    setContext()
    popStyle()

    return img
end
-- ButtonRadioGroupC by Kilam Malik.
--

ButtonRadioGroupC = class()

function ButtonRadioGroupC:init(callback)
    self.btnList = {}
    self.callback = callback
end

function ButtonRadioGroupC:addButtons(...)
    print("addButtons:",self)
    for i, v in ipairs(arg) do
        v.callback = self.btnCallback
        table.insert(self.btnList, v)
    end
end

function ButtonRadioGroupC:setSelected(id)
    for k = 0, table.maxn(self.btnList) do
        if self.btnList[k] == nil then
        else
            local btn = self.btnList[k]
            btn.selected = (btn.id == id)
        end
    end
end

function ButtonRadioGroupC:btnCallback(id, state, btn)
    print("btnCallback:",self,id)
    self:setSelected(id)
    self.callback(id, state, btn)
end

function ButtonRadioGroupC:draw()
    for k = 0, table.maxn(self.btnList) do
        if self.btnList[k] == nil then
        else
            self.btnList[k]:draw()
        end
    end
end

function ButtonRadioGroupC:touched(touch)
    for k = 0, table.maxn(self.btnList) do
        if self.btnList[k] == nil then
        else
            self.btnList[k]:touched(touch)
        end
    end
end
FontC = class()
 
function FontC:init(fntName, fntSize, fntColor, shadowColor, shadowDistance)
    self.fntName = fntName
    self.fntSize = fntSize
    self.fntColor = fntColor
    self.shadowColor = shadowColor
    self.shadowDistance = shadowDistance
end

function FontC:draw(txt)
    pushStyle()
        textMode(CENTER)
        font(self.fntName)
        fontSize(self.fntSize)
 
        -- Move half the shadow distance in each direction to keep the center.
        fill(self.shadowColor)
        text(txt, self.shadowDistance / 2, -self.shadowDistance / 2)
        fill(self.fntColor)
        text(txt, -self.shadowDistance / 2, self.shadowDistance / 2)
    popStyle()
end

Here the callback in the buttons are replaced:

function ButtonRadioGroupC:addButtons(...)
    print("addButtons:",self)
    for i, v in ipairs(arg) do
        v.callback = self.btnCallback
        table.insert(self.btnList, v)
    end
end

And at this point it stops working:

function ButtonRadioGroupC:btnCallback(id, state, btn)
    print("btnCallback:",self,id)
    self:setSelected(id)
    self.callback(id, state, btn)
end

“Attempt to call method ‘setSelected’ (a nil value)”

Calling setSelected from main and also tried from other class functions work. That means, when this callback is called, it is not the same class? I tried to find that out by printing self as you see in this code and then compare it with the print(self) from the addButtons function. But this print does not give me a pointer, it returns “Tog1”, some string I have used as button name… very strange.