Sure, in fact it is probably more natural to take a table. I think it was old habits for me
it started as I was considering writing a quizz type app in which one of the questions was based on a grid. Initially I just made a grid but as I was gluing together a bunch of checkboxes for the multiple choice I thought I could as well hit two birds with one stone.
Maked some changes to fit better in CiderControls. Iāll add to the GIST later, but hereās what I put together so far:
-- the toucharray class takes a string in the latex format rows separated by & and ---
-- columns by \\\\ and produces a touchable array whose topleft corner is is at x,y and whose
-- celsizes are either automatic or given by the user.
-- the typical call is
-- a=touchArray("aaaaaaaa aaaa & b \\\\ c& d & t\\\\ a& 5&", 300,300, "single") or
-- a=touchArray("aaaaaaaa aaaa & b \\\\ c& d & t\\\\ a& 5&", 300,300, "single",50,50) or
-- a=touchArray("aaaaaaaa aaaa & b \\\\ c& d & t\\\\ a& 5&", 300,300)
-- the single variation allows one to choose a unique cell
--the table self.selected is a the set of selected cells, that is the
-- value of self.selected[1..","..2] is true if the cell 1,2 is touched and false otherwise
-- you can check if the cell i,j is selected via self:check(i,j) and
-- you can get the text in the cel i,j by self.array[i][j].text
touchArray = class(Control)
Grid = touchArray --make an alias
function touchArray:init(info, left,top ,seltype, cellsizex, cellsizey, callback)
Control.init(self)
if type(info) == "string" then
self:arrange(info) --creates a table
if cellsizex~=nil then self.celsizex=cellsizex textWrapWidth(cellsizex) end
if cellsizey~=nil then
self.celsizey=math.max(cellsizey,20*self.size/self.celsizex)
else
self.celsizey=math.max(self.celsizey,20*self.size/self.celsizex)
end
else
assert(type(info)== "table" , "Must have a table or LaTeX string to use this code.")
self.tab = info --AC: Note we assuming a table
--AC:assuming the first cell is a model for all other cells. BAAD assumption!
w, h = textSize(info[1][1])
print(w,h)
self.celsizex=math.max(cellsizex, w)
self.celsizey=math.max(cellsizey,h)
--self.celsizex = cellsizex
textWrapWidth(cellsizex)
self.size = self.celsizex
--self.celsizey = self.cellsizey
end
self.selected={}
self.seltype=seltype
self.left = left
self.top = top
maxHeight = 0
maxWidth = 0
self.array={}
for i,v in pairs(self.tab) do
self.array[i]={}
if maxHeight < i then maxHeight = i end
for j,k in pairs(v) do
if maxWidth < j then maxWidth = j end
table.insert(self.array[i],cell(k,self.left+self.celsizex*(j-1),self.top-
self.celsizey*i,self.left+self.celsizex*j,self.top-self.celsizey*(i-1),
callback))
self.selected[i..","..j]=false
end
end
self.bottom = self.top - self.celsizey * maxHeight
self.right = self.left + self.celsizex * maxWidth
self.border = 2 --px border
end
function touchArray:arrange(str)
self.tab={}
self.celsizex=0
self.celsizey=0
i=1
for a in string.gmatch(str,"([^\\\\]+)") do
self.tab[i]={}
for b in string.gmatch(a,"([^&]+)") do
d=b:match "^%s*(.-)%s*$"
w, h = textSize(d)
self.celsizex=math.max(self.celsizex, w)
self.celsizey=math.max(self.celsizey,h)
table.insert(self.tab[i], d)
end
i = i + 1
end
self.size=self.celsizex --cell width?
end
function touchArray:draw()
pushStyle()
fill(self.background)
textWrapWidth(self.celsizex)
rect(self.left-self.border, self.bottom-self.border,
self:width()+self.border*2, self:height()+self.border*2)
popStyle()
for i , v in pairs(self.array) do
for j, k in pairs(v) do
k:draw()
end
end
end
function touchArray:check(i,j)
return self.selected[i..","..j]
end
function touchArray:touched(touch)
for i , v in pairs(self.array) do
for j, k in pairs(v) do
self.selected[i..","..j]=k.selected
k:touched(touch)
if self.seltype=="single" then
if k.selected then
for a,b in pairs(self.array) do
for c,d in pairs(b) do
if d ~= k then d.selected=false
end end end end end
end
end
end
--A cell in the TouchArray
cell=class(Control)
function cell:init(string,x,y,z,w,callback)
Control.init(self,string,x,y,z,w, callback)
self.selected=false
end
function cell:draw()
if self.selected then
self.background =color(175, 22, 47, 255)
else
self.background=color(255, 255, 255, 255)
end
Control.draw(self)
end
function cell:touched(touch)
if self:ptIn(touch.x, touch.y) then
if touch.state == BEGAN then
self.selected = not self.selected
if self.callback ~= nil then self.callback(self) end
end
return true
end
return false
end
--make a click sound when the control is touched
function click(val)
sound(SOUND_HIT, 1960)
if val.selected then
print(val.text)
end
end
Sample code:
assert(Frame ~= nil, "CiderControls not found. Please add the dependency.")
function setup()
aTable= {}
masterTable={}
for x=1, 7 do
for y=1,5 do
--test for holes.
if math.random(10) < 8 then
table.insert(aTable, "testing wrapping text:" ..tostring(x) .. ":" .. tostring(y))
else
table.insert(aTable, nil) --not quite what i want, but good test
end
end
table.insert(masterTable, aTable)
aTable={}
end
a=touchArray("aaaaaaaa aaaa & b \\\\ c& d & t\\\\ a& 5&", 100,300, "Muliple", 75,25 ,click)
b=Grid(masterTable, 300,625, "single", 80,25 ,click)
b.border=5
b.background=color(192,192,192,128) --transparency for fun
end
function draw()
background(194, 194, 194, 255)
a:draw()
b:draw()
end
function touched(touch)
a:touched(touch)
b:touched(touch)
end
Looks very nice, I have no objection to renaming it to Grid
I made an alias to keep compatibility.
@aciolino When we get a little closer to 1.5, Iām going to take your current Git, and replace the portions of Cider that write output to the screen so that it writes directly to a new project. Should be much slicker.
Ok, sounds good. Let me know when so I can make sure we get the right version of several newer controls out there.
@acilino Itās that time. With the impending release of 1.5, I need to modify Cider to use the ability to write code straight to a project tab (especially as text in the output area no longer seems to be selectable).
Can you point me at the most up to date gist? Thanks.
I have the current stuff available here for a short while becuase GIT and GITHUB and I do not seem to see eye to eye. Grabāem from hereā¦
http://dl.dropbox.com/u/24774303/PublicCider/Cider.lua
http://dl.dropbox.com/u/24774303/PublicCider/CiderControls.lua
http://dl.dropbox.com/u/24774303/PublicCider/Menu.lua
http://dl.dropbox.com/u/24774303/PublicCider/TextBox.lua (has issues, not sure why)
I am doing a complete rewrite and merge of two seperate projects to create anintegrated Window manager with controls and menuing. I think thatāll be demoable in a day or two. I bring it up because I am planning on using a large amount of these controls, but fixing some of the design issues with they way they work - they are all absolute coordinates, which makes them somewhat difficult to use in other projects. Not impossible, just difficult.
Cider looks nice. This weekend Iāve been testing to implement a UI similar to QML used in the Qt Library. With my Codea version of this you can write the logic for a button like this:
Button = UI.Item({id="button",
w=100, h=50,
text="Button",
focusColor=color(125,125,250),
color=color(125,125,125)},
UI.TouchArea({id="touchArea", anchors={fill="buttonRect"},
onClicked="parent.onClicked"}),
UI.Rect({id="buttonRect", w="parent.w", h="parent.h",
color=function ()
if touchArea.pressed then
return button.focusColor
else
return button.color
end
end}),
UI.Text({text=function() return parent.text end,
anchors={centerIn="parent"}})
)
The code is quite declarative. You can then use the button component in a project like this:
function setup()
displayMode(FULLSCREEN)
scene2()
end
function scene2()
scene = UI.Item({},
UI.Rect({w="200", x="b2.x",y="b1.y/2",
color=color(100, 183, 91, 255)},
UI.Image({image="Planet Cute:Character Boy",
x=5,y=5,w="parent.w-10",h="parent.h-10"})
),
Button({id="b1", text="Up", y=100, x=10}),
Button({id="b2", text="Right", x="prev.right", y="prev.y"}),
Button({id="b3", text="Reset", x="b1.x", y="b1.top"})
)
local h = scene:id("b1")
h.onClicked = function ()
tween(.2, h, {y=200+h.y})
end
scene:id("b2").onClicked = function ()
tween(.2, h, {x=200+h.x})
end
scene:id("b3").onClicked = function ()
tween(.2, h, {x=10,y=100})
end
end
function draw()
background()
scene:draw()
end
function touched(touch)
scene:touched(touch)
end
You can reference to other objects in lua code, thanks to some use of setmetatable. A bit chaotoc right now, but the gist for the current code is here: https://gist.github.com/4644451
Thanks, @aciolino. Sounds like I need to hang on for a bit and see the next spin you give the project before taking any other steps. I just wanted to use the new saveProjectTab function of 1.5 to write the generated code directly to a temporary project tab, making it easier and neater than the copy/paste out of the output window (and essential under 1.5, because you can no longer copy output).
@tnology That looks very interesting. Something that would probably pay off in the project Iām currently piddling around with (except that, since Iām hustling to get this little 2D game to market, itāll probably go out with the ugly, inheritance-through-duplicate-code mess that Iāve already written).
When we get to the point where Cider is essentially stable in its core design, and working correctly for 1.5, itās my intention to add āone touch formsā ā an option that would allow you to either create a stand along program that would act as a data collection form, or to create the form as a file that can be used by a general purpose forms program.
Interesting, at the end of the day I am doing something very similar to above, or functions are declared is callied in instantiating class.
I have to update the textbox that has problems and doesnāt work correctly.
@aciolino in your latest version, thereās a call to the function saveCode().
Where is this function found? Thanks.
SaveCode can be safely removed. Itās purpose is to back up code to an external service. I archive to Dropbox.
If interested, hereās the classā¦
----
--place this at the top of each tab that needs achiving.
--Must have 4 dashes and no spaces after captureTab for this to operate
--captureTab("filename.lua", captureCode(1))
----
--if you want to achive everything in one file, call in setup()
--saveCode("filename.lua")
-- since all contents in code are concatenated...
captureCode = debug.getinfo -- Give this function a friendly name
local code = "" --concat all code into this string.
codeTabs={} -- store code in a table to write file by file
function captureTab(tabName, tabCode)
if #code > 0 then code = code.."\
\
" end
code = code.."--\
-- "..tabName.."\
--\
"
code = code..string.match(tabCode.source, ".-%-%-%-%-\
(.*)")
table.insert(codeTabs, {tabName, code})
print (tabName.." captured.")
code=""
end
--by this time, code has all of the tabs pasted together.
function saveCode(key)
appKey = "your key" --app key
appSecret = "your secret" --app secret
db = DropBox(appKey, appSecret)
db:CreateFolder(key, function(data)
for k,v in pairs(codeTabs) do
name=v[1]
code=v[2]
print(key.."/"..name)
--printing name below seems to fail!
db:Write(key.."/"..name, code,
function(data) --print( "File written.")
end)
end
end)
end
--example: in each tab add at the top:
--captureTab("main.lua", captureCode(1))
----
--and add to setup() of your target class()
--function setup()
--saveCode("ProjectName")
--end
Sorry if this is a dumb question but I cant figure out how to pull the code out of the generator. It seems that I cant copy it. I believe I have 1.5.1.
Thanks
Codea 1.5 seems to have changed the output window. In 1.4.6, you coulod copy and paste the whole output window. I think @Mark is going to update the original Cider project to alow for writing directly to tas in Codea 1.5.
Iāll be making the same mode to Cider2 later on.
I am new to Codea and Lua and have only done minimal programming in the past. I like what I see with Cider, but cannot get it to run without errors. I created a new project with 4 tabs; Main, Menu, CiderControls, and TextBox and copied/pasted the code from aciolinoās Dropbox links. Should I be doing something else? When I run I get some controls on screen, but touching anything immediately causes an error: attempt to index global āmenu2ā (a nil value).
Please help. I am trying to make a simple Ideal Otto Cycle Model to show my Thermodynamics another example of how they could mathematically model this problem. I would like switches, buttons, and text box inputs to set variables.
Brian
Hereās a version updated for Codea 1.5
Cider
https://gist.github.com/devilstower/5130706
CiderControls
https://gist.github.com/devilstower/5130686
I snuffed a lot of bugs on these and changed output to be routed to a code tab. See what you think.
@Briandbrady If you mix too many peopleās code together all at the same time, you will get problems like this.
As a general programming rule, it is a good idea to start very small and very slowly add complexity. In your case, I would probably start by hard coding all the variables in setup, and printing results in the output window, just to get the core calculations right. Then I would gradually build the user interface one baby step at a time.
The less you change at any time, the easier it is to find your mistakes.
I have been programming as a hobby for a long time, and I use this approach, and I still spend hours trying to find tiny errors.
Thanks Mark and Ignatz. I have a barebones program that is generating basic output and even uses parameters at this time. I am hopeful of switching from using the parameters to vary input values to using interface elements. I am adding one thing at a time though. That is good advice.