Run Projects from a menu

@CodeLearnerMike You might find this interesting. I wrote this because of your Run Multiple Tabs discussion.

Here’s something I threw together to run different projects from a menu. I didn’t put a lot of time into this, so it’s kind of limited. This code example will let you run different Codea projects from a menu screen. When you’re running a project, to return to the Main Menu, press the Main Menu parameter button. You might have to drag open the parameter pane if a project is in fullscreen mode. I don’t have a use for this code which is why I haven’t put a lot into it. Some of the projects that can’t use this are:

  1. Projects that define variables before setup() runs.
  2. Projects that call code before setup() runs.
  3. Projects that call code from another project (calling universal in some of the Codea examples)
  4. Other things that I haven’t found yet.
function setup()
    rectMode(CENTER)
    st1="if not reStarting then reStarting=true setup() end"
    st2="parameter.action('Main Menu',function() restart() end)"
    proj={"Exit","Gravity","Shaders","Roller Coaster","Multi Touch","Ellipse Modes"}
end

function draw()
    background(157, 157, 181, 255)
    for a,b in pairs(proj) do
        fill(0, 206, 254, 255)
        rect(WIDTH/2,100+80*a,120,50)
        fill(247, 22, 22, 255)        
        text(b,WIDTH/2,100+80*a)
    end
end

function touched(t)
    if t.state==BEGAN then
        button(t)
        if str~="" then
            a,b=string.find(str,"function setup()",1,true)
            str=string.sub(str,1,b)..st2..string.sub(str,b+1,#str)
            a,b=string.find(str,"function draw()",1,true)
            str=string.sub(str,1,b)..st1..string.sub(str,b+1,#str)
            loadstring(str)()
        end
    end
end

function button(t)
    for a,b in pairs(proj) do
        str=""
        if t.x>WIDTH/2-60 and t.x<WIDTH/2+60 and t.y>50+a*80 and t.y<150+a*80 then
            if b=="Exit" then 
                close()
            else
                lst=listProjectTabs(b)
                for c,d in pairs(lst) do
                    str=str..readProjectTab(b..":"..d)
                end
                return(str)
            end
        end
    end
end

Here’s another version. I removed the Main Menu parameter button. To return to the Main Menu, just press the restart button at the bottom of the print window.

displayMode(STANDARD)

function setup()
    rectMode(CENTER)
    st1="if not reStarting then reStarting=true setup() end"
    proj={"Exit","00b","Gravity","Shaders","Roller Coaster","Multi Touch","Ellipse Modes"}
end

function draw()
    background(157, 157, 181, 255)
    for a,b in pairs(proj) do
        fill(0, 206, 254, 255)
        rect(WIDTH/2,100+80*a,120,50)
        fill(247, 22, 22, 255)        
        text(b,WIDTH/2,100+80*a)
    end
end

function touched(t)
    if t.state==BEGAN then
        button(t)
        if str~="" then
            a,b=string.find(str,"function draw()",1,true)
            str=string.sub(str,1,b)..st1..string.sub(str,b+1,#str)
            loadstring(str)()
        end
    end
end

function button(t)
    for a,b in pairs(proj) do
        str=""
        if t.x>WIDTH/2-60 and t.x<WIDTH/2+60 and t.y>50+a*80 and t.y<150+a*80 then
            if b=="Exit" then 
                close()
            else
                lst=listProjectTabs(b)
                for c,d in pairs(lst) do
                    str=str..readProjectTab(b..":"..d)
                end
                return(str)
            end
        end
    end
end

@dave1707 Thanks, I’ve learned something new “readProjectTab” that’s what I was looking for, where could I find more commands like that to learn? Sorry if I’m asking too much but do you think you could make this code run a new tab running the AR example? I tried to implement the readProjectTab function but every method I tried I got errors.

--# AR


function setup()
    -- Create a new craft scene
    scene = craft.scene()
    scene.sun:get(craft.light).intensity = 0.7

    if craft.ar.isSupported then
        -- Enable AR session
        scene.ar:run()

        -- Keep a list of detected planes
        planes = {}
        
        -- Option to turn plane detection on and off
        parameter.boolean("PlaneDetection", true, function(b)
            scene.ar.planeDetection = b
        end)
        
        -- Option to draw any detected planes using camera rendering mask
        parameter.boolean("DrawPlanes", true, function(b)
            local c = scene.camera:get(craft.camera)
            if b then
                c.mask = ~0
            else
                c.mask = 1
            end
        end)
        
        parameter.boolean("DrawPointCloud", true)

        local grid = readImage("Project:GridWhite")
        
        scene.ar.didAddAnchors = function(anchors)
            for k,v in pairs(anchors) do
                local p = scene:entity():add(Plane, v, grid)
                planes[v.identifier] = p
            end
        end
        
        scene.ar.didUpdateAnchors = function(anchors)
            for k,v in pairs(anchors) do
                local p = planes[v.identifier]
                p:updateWithAnchor(v)
            end
        end
        
        scene.ar.didRemoveAnchors = function(anchors)
            for k,v in pairs(anchors) do
                local p = planes[v.identifier]
                p.entity:destroy()
                planes[v.identifier] = nil
            end
        end   

        trackingState =
        {
            [AR_NOT_AVAILABLE] = "Not Available",
            [AR_LIMITED] = "Limited",
            [AR_NORMAL] = "Normal"
        }

        cross = image(16,16)
        setContext(cross)
        pushStyle()
        fill(255, 198, 0, 255)
        noStroke()
        rectMode(CENTER)
        rect(cross.width/2, cross.height/2, 3, cross.height)
        rect(cross.width/2, cross.height/2, cross.width, 3)
        popStyle()
        setContext()

    end

end

function update(dt)
    scene:update(dt)   
end

-- Called automatically by codea 
function draw()
    update(DeltaTime)

    -- Draw the scene
    scene:draw()	
    
    local status = nil
    if craft.ar.isSupported then
        status = trackingState[scene.ar.trackingState]
        
        if DrawPointCloud then
            local c = scene.camera:get(craft.camera)
            for k,v in pairs(scene.ar.points) do
                local p = c:worldToScreen(v)
                sprite(cross, p.x, p.y)
            end
        end
    else
        status = "AR Not Supported"
    end
    fill(255, 255, 255, 255)
    text(status, WIDTH/2, HEIGHT - 50)
    
end

function touched(touch)
    if craft.ar.isSupported and touch.state == BEGAN then
        local results = scene.ar:hitTest(
            vec2(touch.x, touch.y),
            AR_EXISTING_PLANE_CLIPPED)
        
        for k,v in pairs(results) do
            local e = scene:entity()
            local cube = e:add(Cube, v.position + vec3(0,0.5,0), 0.1)
            break
        end
    end
end

--# Cube

Cube = class()
function Cube:init(entity,position, size)
self.entity = scene:entity()
self.entity.model = craft.model()
self.entity.position = position
self.entity.scale=vec3(.5,.5,.5)*size
self.entity:add(craft.rigidbody, STATIC, 1)

end 
--# Plane
Plane = class()

function Plane:init(entity, anchor, map)
    self.entity = entity
    self.entity.model = craft.model.plane(vec2(1,1))
    self.entity:get(craft.renderer).mask = 1<<2
    
    local mat = craft.material("Materials:Basic")
    mat.map = map
    mat.blendMode = NORMAL
    mat.diffuse = color(120, 200, 255)
    self.entity.material = mat
    
    -- Collisions   
    self.entity:add(craft.rigidbody, STATIC)
    self.entity:add(craft.shape.box, 
        vec3(1,0.1,1), vec3(0,-0.05,0))
    
    self:updateWithAnchor(anchor, true)
end

function Plane:updateWithAnchor(anchor, s)
    self.entity.position = anchor.position
    self.entity.scale = anchor.extent + vec3(0,1,0)
    self.entity.rotation = anchor.rotation 
    self.entity.material.offsetRepeat = 
        vec4(0,0,anchor.extent.x, anchor.extent.z)   
end

--# Main


function setup() 
    func=menu    -- start screen

    buttonTab={}    -- buttons table

    -- buttons that show on the menu screen
    table.insert(buttonTab,button(350,600,100,50,"Screen 1",menu,screen1))
    table.insert(buttonTab,button(350,500,100,50,"Screen 2",menu,screen2))
    table.insert(buttonTab,button(350,400,100,50,"Screen 3",menu,screen3))
    table.insert(buttonTab,button(700,50,100,50,"EXIT",menu,close))

    -- buttons that show on screen1
    table.insert(buttonTab,button(350,600,150,50,"Google",screen1,url1))
    table.insert(buttonTab,button(350,500,150,50,"twolivesleft",screen1,url2))
    table.insert(buttonTab,button(350,100,150,50,"Menu",screen1,menu))

    -- buttons that show on screen2
    table.insert(buttonTab,button(350,600,150,50,"map quest",screen2,url3))
    table.insert(buttonTab,button(350,100,150,50,"Menu",screen2,menu))

    -- buttons that show on screen3
    table.insert(buttonTab,button(350,600,150,50,"amazon",screen3,url4))    
    table.insert(buttonTab,button(350,100,150,50,"Menu",screen3,menu))
end

function draw()
    background(40, 40, 50)
    func()    -- call the function set by the button
end

function touched(t)    -- check if a button was pressed
    if t.state==BEGAN then
        for a,b in ipairs(buttonTab) do
            if b:touched(t) then
                break
            end                
        end
    end
end

function menu()
    background(58, 97, 136, 255)
    fontSize(30)
    text("Menu Screen",350,700)
    drawButtonTab()
end

function screen1()
    background(101, 43, 43, 255)
    fontSize(30)
    text("Screen 1",350,700)
    drawButtonTab()
end

function screen2()
    background(110, 165, 74, 255)
    fontSize(30)
    text("Screen 2",350,700)
    drawButtonTab()
end

function screen3()
    background(150, 144, 44, 255)
    fontSize(30)
    text("Screen 3",350,700)
    drawButtonTab()
end

function url1()
    openURL('http://www.google.com',true)
    func=screen1    -- return to screen1
end

function url2()
    openURL('http://twolivesleft.com',true)
    func=screen1    -- return to screen2
end

function url3()
    openURL('http://www.mapquest.com',true)
    func=screen2    -- return to screen3
end

function url4()
    readProjectTab("AR")
    func=screen3    -- return to screen4
end

function drawButtonTab()    -- draw selected buttons from button table
    fontSize(20)
    for a,b in ipairs(buttonTab) do
        b:draw()
    end
end    

button=class()

function button:init(x,y,w,h,name,screen,func)
    self.x=x    -- x position
    self.y=y    -- y position
    self.w=w    -- width
    self.h=h    -- height
    self.name=name    -- name to put on the button 
    self.screen=screen -- screen to draw the button on
    self.func=func    -- function to call when the button is pressed
end

function button:draw()
    if func==self.screen then    -- draw the button
        sprite("Cargo Bot:Dialogue Button",self.x,self.y,120)
        fill(255, 0, 0, 255)
        text(self.name,self.x,self.y)
    end
end

function button:touched(t)
    if func==self.screen then    -- only check current screen button 
        if t.x>self.x-self.w/2 and t.x<self.x+self.w/2 and
            t.y>self.y-self.h/2 and t.y<self.y+self.h/2 then
                func=self.func else for function url4 do readProjectTab"AR"
                return true
        end
        return false
    end
end

@CodeLearnerMike - not a mod so I can’t correct but you need to place your ~~~ characters before code and after all code, otherwise a bit messy. Check with the preview option on the comment box before posting.

I get an error from the line

Function=self.func else function url4 do readProjectTab”AR”

Playing around with it I can get it to run but the code has lost functionality.

@CodeLearnerMike I added ~~~ before and after your code so it displays correctly. As for commands, at the top of the forum is a link for Reference. I suggest you look thru that to see what commands are available and what they do.

@dave1707 - another thank you I owe you. Ages ago I posted that Codea would benefit from a chain command (like in many basics) so that you could run separate parts of a project. I didn’t realise you could do it this way.

Do you know if memory is cleared when you run separate projects, if you have graphic intensive apps and low internal memory it could be important.

Other issue, I added a few more projects from the examples and my own projects. They all ran except for ones I had stored in a directory. I couldn’t get projects to run by adding the path. This is odd because the examples in your menu are all in an example folder on my iPad. Weird !!!

@Bri_G I think memory would be cleared because of the restart command. You could try adding collectgarbage(“count”) to show the memory size. I’m not sure of all the restrictions that my code has. It was a quick try and things kind of worked. I didn’t try a lot of different projects to see which ones worked and what was different with the one that didn’t.

@CodeLearnerMike You can’t stick multiple projects into 1 project and expect it to work. In your above code, you have 2 setup(), 2 draw() and 2 touched() functions. The 2nd of each of those functions will override the functions in the first code which means it’s totally useless. The menu part might work somewhat, but the AR code won’t. There’s no point in even trying to fix it.

@dave1707 ok I can understand that, if I got more questions can I message you?

@Bri_G thanks for giving it a try, what did you change that allowed it to run?

@CodeLearnerMike @Bri_G probably changed the code like this

   func=self.func 
    --else for function url4 do readProjectTab"AR"
   return true

which means the menu part of the code worked until you selected Amazon at which point it tried to run readProjectTab(“AR”) which is an invalid tab. I suggest you look thru the reference and read the explanations for the commands you use. readProjectTab() will read the contents of the tab named between the () and return a string.

@CodeLearnerMike - the changes I made are below only partially resolved the problem, it allowed me to access a couple of web pages. Hope this helps you.


function button:touched(t)
    if func==self.screen then -- only check current screen button 
        if t.x>self.x-self.w/2 and t.x<self.x+self.w/2 and
        t.y>self.y-self.h/2 and t.y<self.y+self.h/2 then
             func=self.func 
        else 
    --    for function url4
        --  end
                do readProjectTab"AR"
                    return true
                end
        end
        return false
    end
end