Created
July 7, 2014 08:38
-
-
Save anonymous/30b839d752d2922d0096 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# EventMngr | |
-- ############## START of EVENT MANAGER ################## | |
-- @tnlogy & @JMV38 & @Briarfox | |
-- example of usage: | |
-- EventMngr:extend(evMngr) -- extend an existing table with event manager funcs | |
-- evMngr:on("touch",func) -- register func() to fire on "touch" event | |
-- evMngr:on("touch", obj.func, obj) -- register obj:func() to fire on "touch" event | |
-- evMngr:trigger("touch",10,50) -- fires func(10,50) and obj:func(10,50) | |
-- evMngr:off("touch", func) -- unregister func() | |
-- evMngr:off("touch", obj.func, obj) -- unregister obj:func() | |
-- evMngr:off("touch") -- unregister all "touch" listeners | |
-- evMngr:off(obj.func) -- unregister all listeners with obj.func | |
-- evMngr:off(obj) -- unregister events with obj listening | |
-- "all" captures all events and passes the event name as the first param: | |
-- evMngr:on("all", func) | |
EventMngr = {} | |
local fifo = true -- first in (to register) first out (to be triggered) | |
function EventMngr:on(eventName, fn, obj) | |
if not self.events then self.events = {} end -- init event table if does not exist | |
if not self.events[eventName] then self.events[eventName] = {} end -- init this event name | |
local new = true -- confirm it is a new request | |
for i,fa in ipairs(self.events[eventName]) do | |
if fa.func == fn and fa.obj == obj then new = false end | |
end | |
local p -- insertion point in the table | |
if new then | |
if fifo then p = #self.events[eventName] +1 else p = 1 ; fifo=true end | |
local listener = {func = fn, obj = obj } | |
table.insert(self.events[eventName], p, listener) | |
end | |
return self | |
end | |
function EventMngr:executeNextCallBeforeOthers() | |
fifo = false | |
end | |
function EventMngr:off(nameOrFnOrObj, fn, obj) | |
local name | |
local fn,obj = fn,obj -- manage the case when they are nil | |
local firstType = type(nameOrFnOrObj) | |
local request | |
if firstType == "string" or firstType == "number" then | |
name = nameOrFnOrObj | |
if name == "all" then request = "remove all events" | |
elseif fn == nil then request = "remove all instances of this event" | |
else request = "remove this event" end | |
elseif firstType == "function" then | |
fn = nameOrFnOrObj | |
request = "remove all events with this function" | |
else | |
obj = nameOrFnOrObj | |
request = "remove all events with this object" | |
end | |
if request == "remove all instances of this event" then | |
self.events[name] = nil | |
elseif request == "remove all events" then | |
self.events = {} | |
else | |
local evs = self.events -- go through all events ... | |
if name then evs = {evs[name]} end -- ... or through 1 event only | |
for eventName,fns in pairs(evs) do | |
local n = #fns | |
for i=0,n-1 do | |
local j = n-i -- go backward because of remove, ipairs not suitable | |
local f = fns[j] | |
local match | |
if request == "remove this event" | |
then match=(f.func==fn and f.obj==obj) | |
elseif request == "remove all events with this function" | |
then match=(f.func==fn) | |
elseif request == "remove all events with this object" | |
then match=(f.obj==obj) | |
end | |
if match then | |
table.remove(fns,j) | |
end | |
end | |
end | |
end | |
return self | |
end | |
function EventMngr:trigger(name, ...) | |
self.lastTrigger = name | |
local evs = (self.events and self.events[name]) or {} | |
for i,fa in ipairs(evs) do | |
local func,obj = fa.func, fa.obj | |
if obj then func(obj,...) | |
else func(...) end | |
end | |
--trigger all | |
local evs = (self.events and self.events["all"]) or {} | |
for i,fa in ipairs(evs) do | |
local func,obj = fa.func, fa.obj | |
if obj then func(obj,name,...) | |
else func(name,...) end | |
end | |
end | |
-- to transform a table into an event manager | |
function EventMngr:extend(target) | |
for k, v in pairs(self) do | |
if type(v) == "function" and v ~= EventMngr.extend | |
then target[k] = v | |
end | |
end | |
return target | |
end | |
-- ############## END of EVENT MANAGER ################## | |
--# Saver | |
saver = {} | |
-- a tool to save simple data | |
-- first start the saver in your setup(): creates an exit (and save) button | |
function saver:init() | |
self.button = Box({name="save", x=5, y=5, w=50, h=30, | |
textMode=CENTER, txt="EXIT"}) | |
main:on("draw",self.button.draw,self.button) | |
main:executeNextCallBeforeOthers() | |
main:on("touched",self.button.touched,self.button) | |
self.button:on("tap",self.save,self) | |
self.button:on("tap",function() close() end) | |
end | |
-- then only this command is needed to load and save the object to be saved | |
function saver:load(obj,fields) | |
self:register(obj,fields) | |
local name = obj.name | |
local data = self.data and self.data[name] | |
--obj:update(data) | |
return data | |
end | |
saver.list = {} -- all objects to be saved | |
function saver:register(obj,keys) | |
local name = obj.name | |
if name == nil then error("obj must have a name to be saved") end | |
for _,o in ipairs(self.list) do | |
if o.name == name and o ~= obj then | |
error("this name ("..name..") is already used") | |
end | |
end | |
table.insert(self.list,obj) | |
local fields | |
if keys then | |
fields = {} | |
for i,key in pairs(keys) do fields[key] = true end | |
end | |
self.list[obj] = fields | |
end | |
function saver:saveXYWH() | |
self:save({"x","y","w","h"}) | |
end | |
function saver:save(keys) | |
local txt = {} | |
local function ins(s) table.insert(txt,s) end | |
ins( "saver.data = { \n" ) -- all objects saved in this table | |
local name | |
for i,obj in ipairs(self.list) do | |
name = obj.name | |
local fields = self.list[obj] | |
ins( "['" .. name .. "'] = {") | |
for key,value in pairs(obj) do | |
if (not fields) or fields[key] then | |
local typ = type(value) | |
if typ == "number" or typ == "boolean" then | |
ins( tostring(key) .. " = " .. tostring(value) .. "," ) | |
elseif typ == "string" then | |
ins( tostring(key) .. " = \"" .. tostring(value) .. "\"," ) | |
end | |
end | |
end | |
ins( "}, \n") | |
end | |
ins( "}") | |
txt = table.concat(txt," ") | |
saveProjectTab("data",txt) | |
end | |
--# Table | |
-- Table.lua | |
-- contains various useful functions related to tables | |
Table = class() | |
function Table.remove(array,obj,allInstances) | |
local n = #array | |
for i = n,1,-1 do | |
if array[i] == obj then | |
table.remove(array,i) | |
if not allInstances then return nil end | |
end | |
end | |
end | |
-- shallow clone the array | |
function Table.clone(array) | |
local clone = {} | |
for _,elem in ipairs(array) do table.insert(clone,elem) end | |
return clone | |
end | |
-- applyes this func to all elems of the second argument, which is an array | |
-- func should take as many arguments as the number of arrays that are passed in | |
function Table.map(func,...) | |
assert(arg.n > 0,"Table.map called with no arguments") | |
local result = {} | |
local n = #arg[1] | |
for i = 1,n do | |
-- fixme: isn't there a function that does this for me? | |
local args = {} | |
for _,arr in ipairs(arg) do table.insert(args,arr[i]) end | |
local r = func(unpack(args)) | |
table.insert(result,r) | |
end | |
return result | |
end | |
function Table.random(array) | |
local n = #array | |
local r = math.random(n) | |
return array[r] | |
end | |
function Table.contains(array,obj) | |
for _,elem in ipairs(array) do | |
if elem == obj then return true end | |
end | |
return false | |
end | |
function Table.iPrevious(array,obj) | |
for i,elem in ipairs(array) do | |
if elem == obj then return array[i-1] end | |
end | |
return false | |
end | |
function Table.iNext(array,obj) | |
for i,elem in ipairs(array) do | |
if elem == obj then return array[i+1] end | |
end | |
return false | |
end | |
function Table.size(tab) | |
local n = 0 | |
for k,v in pairs(tab) do n = n + 1 end | |
return n | |
end | |
--# Stack | |
-- Stack.lua | |
-- a generic stack class. Piles of blocks are stacks. | |
-- Also used to keep track of the user program stack | |
Stack = class() | |
function Stack:init() | |
self.elems = {} | |
end | |
-- removes and return the top elem | |
function Stack:pop() | |
local elem = self:peek() | |
table.remove(self.elems,#self.elems) | |
return elem | |
end | |
-- adds a new elem at the top | |
function Stack:push(elem) | |
table.insert(self.elems,elem) | |
end | |
-- returns the last elem, but doesn't remove it | |
function Stack:peek(idx) | |
idx = idx or #self.elems | |
return self.elems[idx] | |
end | |
function Stack:size() | |
return #self.elems | |
end | |
-- returns an iterator over the elems of the stack | |
function Stack:iter() | |
local i = 0 | |
local n = self:size() | |
return function() | |
i = i + 1 | |
if i <= n then return self.elems[i] end | |
end | |
end | |
--# PositionObj | |
-- PositionObj.lua | |
-- a helper class that is subclassed by anything that has a position | |
PositionObj = class() | |
function PositionObj:init(x,y) | |
self.x = x | |
self.y = y | |
end | |
function PositionObj:setXY(x,y) | |
self.x = x | |
self.y = y | |
end | |
function PositionObj:translate(dx,dy) | |
self.x = self.x + dx | |
self.y = self.y + dy | |
end | |
function PositionObj:getX() | |
return self.x | |
end | |
function PositionObj:getY() | |
return self.y | |
end | |
function PositionObj:getPos() | |
return self.x,self.y | |
end | |
--# RectObj | |
-- RectObj.lua | |
-- RectObj is a helper class that is used by things that have dimensions and angles | |
RectObj = class(PositionObj) | |
function RectObj:init(x,y,w,h) | |
PositionObj.init(self,x,y) | |
self.w = w | |
self.h = h | |
self.mode = CORNER | |
self.angle = 0 -- in rads | |
end | |
---------------------- SETTERS ----------------------- | |
function RectObj:setMode(mode) | |
self.mode = mode | |
end | |
function RectObj:setSize(w,h) | |
self.w = w | |
self.h = h | |
end | |
-- ang in degrees | |
function RectObj:rotate(ang) | |
self.angle = self.angle + math.rad(ang) | |
end | |
function RectObj:setAngle(ang) | |
self.angle = math.rad(ang) | |
end | |
-- flips along the x-axis | |
function RectObj:flipX() | |
self.x = self.x + self.w | |
self.w = self.w * (-1) | |
end | |
---------------------- GETTERS ----------------------- | |
function RectObj:getW() | |
return self.w | |
end | |
function RectObj:getH() | |
return self.h | |
end | |
function RectObj:getSize() | |
return self.w,self.h | |
end | |
function RectObj:getAngle() | |
return math.deg(self.angle) | |
end | |
function RectObj:inbounds(t) | |
local x1,y1,x2,y2 = self:boundingBox() | |
return (t.x>=x1 and t.y>=y1 and t.x<=x2 and t.y<=y2) | |
end | |
function RectObj:boundingBox() | |
local x,y = self.x,self.y | |
local w,h = self.w,self.h | |
if w < 0 then | |
w = -w | |
x = x - w | |
end | |
if h < 0 then | |
h = -h | |
y = y - h | |
end | |
if mode == CENTER then | |
x = x - w/2 | |
y = y - h/2 | |
end | |
return x,y,x+w,y+h | |
end | |
--# Xtouch | |
Xtouch = class() | |
-- a helper class to extend touch (no instance created) | |
-- generates these events: | |
-- onTap, onDrag, onDrop, onEnter, onLeave, onSwipeRight, onSwipeLeft, onSwipeUp, onSwipeDown | |
local touches = {} | |
function Xtouch:extend(touch) | |
-- simple copy | |
local t = {} | |
t.id = touch.id | |
t.x = touch.x | |
t.y = touch.y | |
t.prevX = touch.prevX | |
t.prevY = touch.prevY | |
t.deltaX = touch.deltaX | |
t.deltaY = touch.deltaY | |
t.state = touch.state | |
t.tapCount = touch.tapCount | |
-- extensions | |
t.time = ElapsedTime -- knowing the touch time may be usefull | |
t.intercepted = false -- to manage touch interception | |
t.beganOnObj = false -- object on which the touch began (if any) | |
t.movingOnObj = false -- object on which the touch is moving (if any) | |
t.endedOnObj = false -- object on which the touch ended (if any) | |
t.prevOnObj = false -- object touched last time, if any | |
t.curOnObj = false -- object currently touched, if any | |
t.Xevent = false -- alternative state | |
-- memorize the began touches (or current one if no began) | |
if t.state == BEGAN or touches[t.id] == nil then touches[t.id] = t end | |
return t | |
end | |
function Xtouch:updateFromTouchedObject(t,obj) | |
if t.state == BEGAN then t.beganOnObj=obj | |
elseif t.state == MOVING then t.movingOnObj=obj | |
elseif t.state == ENDED then t.endedOnObj=obj | |
end | |
t.curOnObj = obj | |
t.intercepted = true | |
end | |
function Xtouch:events(t) | |
local t0 = touches[t.id] | |
t.beganOnObj = t0.beganOnObj | |
t.prevOnObj = t0.prevOnObj | |
t0.prevOnObj = t.curOnObj | |
if t.state == BEGAN then | |
local obj = t.beganOnObj | |
t.Xevent = "onEnter" | |
if obj then obj:Xtouched(t) end | |
elseif t.state == MOVING then | |
local obj1 = t.beganOnObj | |
local obj2 = t.prevOnObj | |
local obj3 = t.curOnObj | |
t.Xevent = "onLeave" | |
if obj2 and (obj3~=obj2) then obj2:Xtouched(t) end | |
t.Xevent = "onEnter" | |
if obj3 and (obj3~=obj2) then obj3:Xtouched(t) end | |
t.Xevent = "onDrag" | |
if obj1 then obj1:Xtouched(t) end | |
elseif t.state == ENDED then | |
local obj1 = t.beganOnObj | |
local obj2 = t.prevOnObj | |
local obj3 = t.curOnObj | |
local obj4 = t.endedOnObj | |
t.Xevent = "onLeave" | |
if obj2 and (obj3~=obj2) then obj2:Xtouched(t) end | |
if obj3 and (obj3==obj2) then obj3:Xtouched(t) end | |
if (t.time-t0.time)<1.0 then | |
local v = vec2(t.x-t0.x, t.y-t0.y) | |
local d = v:len() | |
local maximumDistanceForATap = 50 | |
if d < maximumDistanceForATap then | |
t.Xevent = "onTap" | |
if obj4 == obj1 and obj4 then obj4:Xtouched(t) end | |
end | |
local minimumDistanceForASwipe = 75 | |
if d > minimumDistanceForASwipe and obj1 then | |
v = v:normalize() | |
if v.x > 0.8 then | |
t.Xevent = "onSwipeRight" | |
elseif v.x < -0.8 then | |
t.Xevent = "onSwipeLeft" | |
elseif v.y > 0.8 then | |
t.Xevent = "onSwipeUp" | |
elseif v.y < -0.8 then | |
t.Xevent = "onSwipeDown" | |
end | |
if obj1 then obj1:Xtouched(t) end | |
end | |
end | |
if obj4 ~= obj1 then | |
t.Xevent = "onDrop" | |
if obj1 and obj4 then obj4:Xtouched(t) end | |
end | |
elseif t.state == CANCELLED then | |
local obj1 = t.beganOnObj | |
local obj2 = t.prevOnObj | |
t.Xevent = "onLeave" | |
if obj2 then obj2:Xtouched(t) | |
elseif obj1 then obj1:Xtouched(t) | |
end | |
t.Xevent = "onDrop" | |
if obj1 then obj1:Xtouched(t) end | |
end | |
-- erase the finished touches | |
if t.state == ENDED or t.state == CANCELLED then touches[t.id] = nil end | |
end | |
--# Touchable | |
-- Touchable.lua | |
-- a little useful class for objects that know how to handle touches | |
Touchable = class(RectObj) | |
function Touchable:init(name,x,y,w,h) | |
RectObj.init(self,x,y,w,h) | |
self.name = name | |
self.touchable = true | |
self.extras = {left=0,right=0,top=0,bottom=0} | |
end | |
function Touchable:setTouchable(bool) | |
self.touchable = bool | |
end | |
function Touchable:getTouchable() | |
return self.touchable | |
end | |
function Touchable:setExtras(extras) | |
for k,v in pairs(extras) do | |
self.extras[k] = v | |
end | |
end | |
function Touchable:listenToTouchedFrom(screen) | |
screen:executeNextCallBeforeOthers() -- obj on top (last) must be checked first | |
screen:on("touched", self.touched, self) | |
end | |
-- standard touch events | |
function Touchable:onTouched(t) end -- user defined | |
function Touchable:onEnded(t) end -- user defined | |
function Touchable:onBegan(t) end -- user defined | |
function Touchable:onMoving(t) end -- user defined | |
--[[ | |
-- extended touch events | |
function Touchable:onTap(t) end -- user defined | |
function Touchable:onDrag(t) end -- user defined | |
function Touchable:onDrop(t) end -- user defined | |
function Touchable:onEnter(t) end -- user defined | |
function Touchable:onLeave(t) end -- user defined | |
--]] | |
function Touchable:touched(t) | |
if t.intercepted then return false end | |
if not self.touchable then return false end | |
if self:inbounds(t) then | |
self:onTouched(t) | |
if t.state == BEGAN then self:onBegan(t) | |
elseif t.state == MOVING then self:onMoving(t) | |
elseif t.state == ENDED then self:onEnded(t) | |
end | |
Xtouch:updateFromTouchedObject(t,self) | |
return true | |
end | |
return false | |
end | |
function Touchable:Xtouched(t) | |
local func = self[ t.Xevent ] | |
if func then func( self, t) end | |
end | |
function Touchable:inbounds(t) | |
local x1,y1,x2,y2 = self:boundingBox() | |
x1 = x1 - self.extras.left | |
y1 = y1 - self.extras.bottom | |
x2 = x2 + self.extras.right | |
y2 = y2 + self.extras.top | |
return (t.x>=x1 and t.y>=y1 and t.x<=x2 and t.y<=y2) | |
end | |
--# StyleObj | |
StyleObj = class() | |
-- a helper class to add a style component | |
local mem = {} | |
local echo = false | |
function StyleObj:set(field,style,default) | |
style = style or {} | |
local value = style[field] | |
if value == nil then value = self[field] end | |
if value == nil then value = default end | |
self[field] = value | |
if echo then mem[field] = self[field] end | |
end | |
-- a setStyle function must be implemented by user, using set() function only | |
function StyleObj:getStyle() | |
mem = {} -- reset mem | |
echo = true -- start echoing | |
self:setStyle() -- now mem contains the style fields and their values | |
echo = false -- stop echoing | |
local style = mem | |
mem = {} -- change mem table to avoid modifying style later by accident | |
return style | |
end | |
function StyleObj:pushStyle() | |
self.oldStyle = self:getStyle() | |
end | |
function StyleObj:popStyle() | |
self:setStyle(self.oldStyle) | |
end | |
function StyleObj:mixin(target) | |
-- inject these functions to the target, unless they are already defined | |
target.set = target.set or self.set | |
target.getStyle = target.getStyle or self.getStyle | |
target.pushStyle = target.pushStyle or self.pushStyle | |
target.popStyle = target.popStyle or self.popStyle | |
end | |
--# RectDraw | |
RectDraw = class(RectObj) | |
-- component class to define and draw a rectangle | |
function RectDraw:init(x,y,w,h,style) | |
RectObj.init(self,x,y,w,h) | |
StyleObj:mixin(self) | |
self:setStyle(style) | |
end | |
function RectDraw:setStyle(style) | |
self:set("fill",style,color(0,0,0,0)) | |
self:set("stroke",style,color(255)) | |
self:set("strokeWidth",style,2) | |
self:set("smooth",style,noSmooth) | |
end | |
function RectDraw:draw() | |
self.smooth() | |
fill(self.fill) | |
stroke(self.stroke) | |
strokeWidth(self.strokeWidth) | |
rect(self.x,self.y,self.w,self.h) | |
end | |
--# RectSprite | |
RectSprite = class(RectObj) | |
-- this rect can have rounded corners | |
function RectSprite:init(x,y,w,h,style) | |
RectDraw.init(self,x,y,w,h,style) | |
StyleObj:mixin(self) | |
self.drawImagePending = true | |
self:setStyle(style) | |
end | |
function RectSprite:setSize(w,h) | |
RectObj.setSize(self,w,h) | |
self.drawImagePending = true | |
end | |
function RectSprite:setStyle(style) | |
self:set("fill",style,color(0,0,0,0)) | |
self:set("stroke",style,color(255)) | |
self:set("strokeWidth",style,3) | |
self:set("smooth",style,smooth) | |
self:set("radius",style,2) | |
self.drawImagePending = true | |
end | |
function RectSprite:draw() | |
spriteMode(CORNER) | |
if self.img then sprite(self.img,self.x,self.y) end | |
self:drawImage() -- update with 1 frame delay: better smoothness | |
end | |
local function rawRect(img,x,y,w,h,r) | |
setContext(img) | |
background(0,0,0,0) | |
fill(255) | |
translate(x,y) | |
rect(r,0,w-2*r,h) | |
rect(0,r,w,h-2*r) | |
ellipse(r ,r,r*2) | |
ellipse(w-r,r,r*2) | |
ellipse(r ,h-r,r*2) | |
ellipse(w-r,h-r,r*2) | |
resetMatrix() | |
setContext() | |
end | |
function RectSprite:roundedRect(x,y,w,h,r) | |
local oldFill = color( fill() ) | |
local oldStroke = color( stroke() ) | |
local oldTint = color( tint() ) | |
local s = strokeWidth() | |
local ds = math.max(s-1,0) | |
local img = image(w,h) | |
local imgOuter = image(w,h) | |
local imgInner = image(w,h) | |
local imgStroke = image(w,h) | |
pushStyle() | |
resetStyle() | |
pushMatrix() | |
resetMatrix() | |
spriteMode(CORNER) | |
noStroke() | |
rawRect(imgOuter,0,0,w,h,r) | |
rawRect(imgInner, ds, ds, w-2*ds, h-2*ds, r-ds) | |
setContext(imgStroke) | |
sprite(imgOuter,0,0) | |
blendMode(ZERO, ONE_MINUS_SRC_COLOR) | |
sprite(imgInner,0,0) | |
setContext() | |
blendMode(NORMAL) | |
setContext(img) | |
tint(oldStroke) | |
sprite(imgStroke,0,0) | |
tint(oldFill) | |
sprite(imgInner,0,0) | |
setContext() | |
popMatrix() | |
popStyle() | |
return img | |
end | |
function RectSprite:drawImage() | |
-- the main goal of this "pending" is to avoid multiple unecessary redraw (1 only is needed) | |
if not self.drawImagePending then return end | |
local img = self.img | |
if img == nil or img.width ~= self.w or img.height ~= self.h then | |
self.img = image(self.w, self.h) | |
end | |
self.smooth() | |
fill(self.fill) | |
stroke(self.stroke) | |
strokeWidth(self.strokeWidth) | |
self.img = self:roundedRect(0,0,self.w,self.h,self.radius) | |
self.drawImagePending = false | |
end | |
--# RRectObj | |
RRectObj = class(RectObj) | |
-- this is a rect with rounded corners, based on a mesh to keep mem low | |
function RRectObj:init(x,y,w,h,style) | |
RectDraw.init(self,x,y,w,h,style) | |
StyleObj:mixin(self) | |
self:setStyle(style) | |
self:meshCreate() | |
self.meshUpdatePending = true | |
self:meshUpdate() | |
end | |
function RRectObj:setSize(w,h) | |
RectObj.setSize(self,w,h) | |
self.meshUpdatePending = true | |
end | |
function RRectObj:setStyle(style) | |
self:set("fill",style,color(0,0,0,0)) | |
self:set("stroke",style,color(255)) | |
self:set("strokeWidth",style,2) | |
self:set("smooth",style,smooth) | |
if style and style.radius and style.radius <1 then style.radius = 1 end | |
self:set("radius",style,10) | |
self.meshUpdatePending = true | |
end | |
function RRectObj:draw() | |
pushMatrix() | |
translate(self.x,self.y) | |
self.mesh:draw() | |
self:meshUpdate() -- update with 1 frame delay: better smoothness | |
popMatrix() | |
end | |
function RRectObj:meshUpdate() | |
-- the main goal of this "pending" is to avoid multiple unecessary redraw (1 only is needed) | |
if not self.meshUpdatePending then return end | |
self:meshResize(self.w,self.h,self.radius,self.strokeWidth) | |
self:meshTexture(self.radius) | |
self.meshUpdatePending = false | |
end | |
function RRectObj:meshTexture(r) | |
d = math.max( math.floor(r), 2) | |
local w,h = 2*d,2*d | |
local img = image(w,h) | |
pushMatrix() pushStyle() | |
resetMatrix() resetStyle() | |
setContext(img) | |
self.smooth() | |
fill(self.fill) | |
stroke(self.stroke) | |
strokeWidth(self.strokeWidth) | |
if r >= 2 then | |
ellipseMode(CORNER) | |
ellipse(0,0,w,h) | |
else | |
rectMode(CORNER) | |
rect(0,0,w,h) | |
end | |
setContext() | |
popMatrix() popStyle() | |
self.mesh.texture = img | |
end | |
function RRectObj:meshCreate() | |
-- the rect are fake and need resizing | |
local i | |
local m = mesh() | |
local x0,x1,x2 = 0,0.5,0.5 | |
local w0,w1,w2 = 0.5,0,0.5 | |
local y0,y1,y2 = 0,0.5,0.5 | |
local h0,h1,h2 = 0.5,0,0.5 | |
-- bottom line | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x0,y0,w0,h0) | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x1,y0,w1,h0) | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x2,y0,w2,h0) | |
-- middle line | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x0,y1,w0,h1) | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x1,y1,w1,h1) | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x2,y1,w2,h1) | |
-- top line | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x0,y2,w0,h2) | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x1,y2,w1,h2) | |
i = m:addRect(0,0,1,1) | |
m:setRectTex(i,x2,y2,w2,h2) | |
self.mesh = m | |
end | |
function RRectObj:meshResize(w,h,r,strkWidth) | |
-- texture image size must be 2rx2r | |
local floor, ceil, min, max = math.floor, math.ceil, math.min, math.max | |
w,h = ceil(w), ceil(h) | |
r = min( floor(r), floor(w/2), floor(h/2) ) | |
local d = r | |
local x0,x1,x2 = d/2, w/2, w-d/2 | |
local w0,w1,w2 = d, w-2*d, d | |
local y0,y1,y2 = d/2, h/2, h-d/2 | |
local h0,h1,h2 = d, h-2*d, d | |
local i | |
local m = self.mesh | |
-- bottom line | |
i=1 | |
m:setRect(i,x0,y0,w0,h0) | |
i=i+1 | |
m:setRect(i,x1,y0,w1,h0) | |
i=i+1 | |
m:setRect(i,x2,y0,w2,h0) | |
-- middle line | |
i=i+1 | |
m:setRect(i,x0,y1,w0,h1) | |
i=i+1 | |
m:setRect(i,x1,y1,w1,h1) | |
i=i+1 | |
m:setRect(i,x2,y1,w2,h1) | |
-- top line | |
i=i+1 | |
m:setRect(i,x0,y2,w0,h2) | |
i=i+1 | |
m:setRect(i,x1,y2,w1,h2) | |
i=i+1 | |
m:setRect(i,x2,y2,w2,h2) | |
end | |
--# ImgObj | |
ImgObj = class(RectObj) | |
function ImgObj:init(x,y,w,h,style) | |
RectDraw.init(self,x,y,w,h,style) | |
StyleObj:mixin(self) | |
self:setStyle(style) | |
self.img = false -- needs load | |
end | |
function ImgObj:load(file) | |
if file then self.file = file end | |
local a = self.innerMargin | |
local w,h = self.w, self.h | |
self.img = image(w,h) | |
setContext(self.img) | |
pushMatrix() pushStyle() | |
resetMatrix() resetStyle() | |
spriteMode(CORNER) | |
sprite(self.file, a, a, w-2*a, h-2*a) | |
popMatrix() popStyle() | |
setContext() | |
end | |
function ImgObj:setSize(w,h) | |
RectObj.setSize(self,w,h) | |
self:load(self.file) | |
end | |
function ImgObj:setStyle(style) | |
self:set("innerMargin",style,0) | |
end | |
function ImgObj:draw() | |
if not self.img then return end | |
pushMatrix() | |
spriteMode(CORNER) | |
translate(self.x,self.y) | |
sprite(self.img,0,0) | |
popMatrix() | |
end | |
--# TextObj | |
TextObj = class(PositionObj) | |
-- component class to define and draw a text | |
function TextObj:init(txt,x,y,style) | |
PositionObj.init(self, x or 0, y or 0) | |
self.value = txt or "" | |
StyleObj:mixin(self) | |
self:setStyle(style) | |
end | |
function TextObj:setText(txt) | |
self.value = txt or "" | |
end | |
function TextObj:getText() | |
return self.value | |
end | |
function TextObj:setStyle(style) | |
self:set("font",style,"Futura-CondensedExtraBold") | |
self:set("fontSize",style,30) | |
self:set("fill",style,color(255)) | |
self:set("textMode",style,CENTER) | |
self:set("textWrapWidth",style,-1) | |
self:set("textAlign",style,CENTER) | |
end | |
function TextObj:draw() | |
pushStyle() | |
pushMatrix() | |
translate(self.x, self.y) | |
smooth() | |
font(self.font) | |
fontSize(self.fontSize) | |
fill(self.fill) | |
textMode(self.textMode) | |
textWrapWidth(self.textWrapWidth) | |
textAlign(self.textAlign) | |
text(self.value) | |
popMatrix() | |
popStyle() | |
end | |
--# Button | |
Button = class(Touchable) | |
-- this is a button with text inside and can draw itself | |
function Button:init(name,x,y,w,h,style) | |
w = w or 200 | |
h = h or 50 | |
x = x or WIDTH/2 -w/2 | |
y = y or HEIGHT/2 -h/2 | |
Touchable.init(self,name,x,y,w,h) | |
StyleObj:mixin(self) | |
self:setStyle(style) | |
self:load() | |
self.rect = RRectObj(0,0,w,h) -- rect background | |
self.img = ImgObj(0,0,w,h) -- an image object (no image until self.img:load(file)) | |
self.txt = TextObj(name, w/2, h/2, {textWrapWidth = w-10}) -- text inside | |
self:setTint() | |
self:setRectVisible(true) | |
self:setImgVisible(true) | |
self:setTxtVisible(true) | |
self:setBordVisible(false) | |
self.visible = true | |
if self.listenToEditor then self:listenToEditor() end | |
end | |
function Button:load() | |
if self.save ~= true then return end | |
local data = saver:load(self,{"x","y"}) | |
if data == nil then return end | |
local x,y = data.x, data.y | |
self:setXY(x,y) | |
end | |
function Button:onEnter(t) | |
self.rect:pushStyle() | |
self.rect:setStyle({ fill = self.onEnterColor }) | |
end | |
function Button:onLeave(t) | |
self.rect:popStyle() | |
end | |
function Button:addToScreen(screen) | |
screen:add(self) | |
self:listenToDrawFrom(screen) | |
self:listenToTouchedFrom(screen) | |
end | |
function Button:listenToDrawFrom(screen) | |
screen:on("draw", self.draw, self) | |
end | |
function Button:draw() | |
tint(self.tint) | |
pushMatrix() | |
translate(self.x, self.y) | |
if self.visible then | |
if self.rectVisible then self.rect:draw() end | |
if self.imgVisible then self.img:draw() end | |
if self.txtVisible then self.txt:draw() end | |
end | |
if self.bordVisible then self.bord:draw() end | |
popMatrix() | |
end | |
function Button:dragMe(t) | |
self:translate(t.deltaX,t.deltaY) | |
end | |
function Button:doNothing(t) end | |
---------------------- SETTERS ----------------------- | |
function Button:setVisible(bool) | |
self.visible = bool | |
end | |
function Button:getVisible() | |
return self.visible | |
end | |
function Button:setStyle(style) | |
style = style or {} | |
if style.rect then self.rect:setStyle(style.rect) end | |
if style.txt then self.txt:setStyle(style.txt) end | |
self:set("onEnterColor", style, color(0, 137, 255, 255) ) | |
local defaultSave = true | |
if self.name == nil then defaultSave = false end | |
self:set("save", style, defaultSave ) | |
self:set("hasImg", style, true ) | |
self:set("hasRect", style, true ) | |
self:set("hasText", style, true ) | |
end | |
function Button:getStyle() | |
style = {} | |
style.rect = self.rect:getStyle() | |
style.txt = self.txt:getStyle() | |
style.onEnterColor = self.onEnterColor | |
style.save = self.save | |
return style | |
end | |
function Button:setTint(c) | |
self.tint = c or color(255) | |
end | |
function Button:setRectVisible(bool) | |
self.rectVisible = bool | |
end | |
function Button:setImgVisible(bool) | |
self.imgVisible = bool | |
end | |
function Button:setTxtVisible(bool) | |
self.txtVisible = bool | |
end | |
function Button:setBordVisible(bool) | |
self.bordVisible = bool | |
if bool then | |
local w,h = self.w, self.h | |
self.bord = RectDraw(-5,-5,w+10,h+10,{strokeWidth=5,stroke=color(255,0,0)}) -- rect around | |
else | |
self.bord = nil | |
end | |
end | |
--# presetButtons | |
-- presetButtons: some 'ready to use' buttons | |
-- adds a uniform background button to screen, with a swipe method to change screen | |
function backgroundButton( screen, backColor ) | |
local b | |
b = Button("no name",0,0,WIDTH,HEIGHT,{save=false}) | |
b.rect:setStyle({strokeWidth=0,radius=1,fill=backColor}) | |
b:setTxtVisible(false) | |
b:addToScreen(screen) | |
b.onEnter = b.doNothing | |
b.onLeave = b.doNothing | |
b.onSwipeLeft = function(self,t) screen:openNext() end | |
b.onSwipeRight = function(self,t) screen:openPrevious() end | |
return b | |
end | |
-- adds a button just displaying a long text | |
function longTextButton( screen, name, w,h, txtStyle, txt ) | |
local b = Button(name,WIDTH/2,HEIGHT/2,50,50) | |
b:addToScreen(screen) | |
b:setTouchable(false) | |
b:setRectVisible(false) | |
b.txt:setStyle( txtStyle ) | |
b.txt:setStyle( {textWrapWidth=WIDTH} ) | |
b.txt:setText( txt ) | |
return b | |
end | |
-- adds a button just displaying a line of text | |
function noWrapButton( screen, name, w,h, txtStyle, txt ) | |
local b = Button(name,WIDTH/2,HEIGHT/2,50,50) | |
b:addToScreen(screen) | |
b:setTouchable(false) | |
b:setRectVisible(false) | |
b.txt:setStyle( txtStyle ) | |
b.txt:setStyle( {textWrapWidth=-1} ) | |
b.txt:setText( txt ) | |
return b | |
end | |
--# Panel | |
-- Panel.lua | |
-- A panel is a container for other panels or objects that can be drawn. Used to form a | |
-- hierarchical representation of what's in the screen. | |
-- - The leaves of the hierarchy are typically SpriteObjs | |
-- - Handles binding and unbinding of events | |
-- - Forwards things like touched and collided to its elements | |
Panel = class(PositionObj) | |
function Panel:init(x,y) | |
PositionObj.init(self,x,y) | |
EventMngr:extend(self) | |
self.touchable = true -- if not active, touch events are not handled | |
self.visible = true | |
self.elems = {} | |
end | |
-- object should have coordinates relative to this one | |
function Panel:add(obj) | |
Table.map(function(x) assert(x~=obj,"adding duplicate obj") end,self.elems) | |
-- obj:translate(self.x,self.y) -- changed by Jmv38: not compatible with the saver process | |
table.insert(self.elems,obj) | |
end | |
function Panel:remove(obj) | |
Table.remove(self.elems,obj) | |
end | |
function Panel:removeAll() | |
self.elems = {} | |
end | |
function Panel:translate(dx,dy) | |
PositionObj.translate(self,dx,dy) | |
Table.map(function(x) x:translate(dx,dy) end,self.elems) | |
end | |
function Panel:setActive(bool) | |
self.touchable = bool | |
end | |
function Panel:setTint(c) | |
Table.map(function(x) if x.setTint then x:setTint(c) end end, self.elems) | |
end | |
function Panel:touched(t) | |
if not self.touchable then return nil end | |
local elemsClone = Table.clone(self.elems) | |
Table.map(function(x) if x.touched and x.touchable then x:touched(t) end end,elemsClone) | |
end | |
--# Screen | |
Screen = class(Panel) | |
-- sends commands: open, close, update, draw, touched | |
-- extends the touch | |
local currentScreen | |
local nextScreen -- for transitions | |
local screens = {} | |
function Screen:init(style) | |
Panel.init(self,0,0) | |
StyleObj:mixin(self) | |
table.insert(screens,self) | |
self:on("draw",function() resetMatrix() translate(self.offsetX,self.offsetY) end) | |
-- self.background = RRectObj(0,0,WIDTH,HEIGHT,{strokeWidth=0,radius=1}) | |
-- self:on("draw",self.background.draw,self.background) | |
-- self:createBackgroundButton() | |
self:setStyle(style) | |
self:setOffset(0,0) | |
end | |
function Screen:setStyle(style) | |
style = style or {} | |
-- self:set("backgroundColor", style, color(32,32,32, 255) ) | |
self:set("transitionComming", style, "fromRight" ) | |
-- self.background:setStyle({ fill = self.backgroundColor }) | |
-- self.background.rect:setStyle({ fill = self.backgroundColor }) | |
end | |
function Screen:setOffset(offsetX,offsetY) | |
self.offsetX, self.offsetY = offsetX or 0, offsetY or 0 | |
end | |
-- call when computations are needed | |
function Screen:update() | |
if currentScreen then currentScreen:trigger("update") end | |
if nextScreen then nextScreen:trigger("update") end | |
end | |
function Screen:draw() | |
background(0) | |
if currentScreen then currentScreen:trigger("draw") end | |
if nextScreen then nextScreen:trigger("draw") end | |
end | |
function Screen:touched(t) | |
if not currentScreen or not currentScreen.touchable then return end | |
local t = Xtouch:extend(t) | |
currentScreen:trigger("touched",t) | |
Xtouch:events(t) -- must be called after a trigger("touched",t) to fill some fields of t | |
end | |
-- actions to be done when opening a screen | |
function Screen:open(transition) | |
nextScreen = self | |
nextScreen:trigger("open") | |
local dx = 0 | |
transition = transition or self.transitionComming | |
if transition == "fromRight" then dx = WIDTH | |
elseif transition == "fromLeft" then dx = -WIDTH | |
end | |
nextScreen:setOffset(dx,0) | |
-- make untouchable | |
if currentScreen then currentScreen:setTouchable(false) end | |
if nextScreen then nextScreen:setTouchable(false) end | |
-- make animation | |
local easing = tween.easing.quartInOut | |
local duration = 0.5 | |
if currentScreen then | |
tween(duration,currentScreen,{offsetX=-dx,offsetY=0},easing) | |
end | |
tween(duration,self,{offsetX=0,offsetY=0},easing,function() | |
if currentScreen then | |
-- currentScreen:setTouchable(true) | |
currentScreen:close() | |
end | |
currentScreen = nextScreen | |
currentScreen:setTouchable(true) | |
nextScreen = nil | |
end) | |
end | |
-- close current screen | |
function Screen:close() | |
currentScreen:trigger("close") | |
-- tween.delay(0.1, function() saver:save() end ) | |
end | |
function Screen:setTouchable(bool) | |
self.touchable = bool | |
end | |
function Screen:getTouchable() | |
return self.touchable | |
end | |
function Screen:openNext() | |
local s = Table.iNext(screens,self) | |
if s then s:open( "fromRight" ) end | |
end | |
function Screen:openPrevious() | |
local s = Table.iPrevious(screens,self) | |
if s then s:open( "fromLeft" ) end | |
end | |
--# Editor | |
Editor = class() | |
function Editor:init(show) | |
EventMngr:extend(self) | |
self.button = Button("editor",WIDTH-200,HEIGHT-100,150,50) | |
self.button:listenToEditor() | |
self.button:setStyle({save=true}) | |
self.button.txt:setStyle({fontSize=20}) | |
self:setState(false) | |
self.button.onTap = function(b,t) | |
self:setState( not self.state ) | |
end | |
self:setVisible(show) | |
end | |
function Editor.addEditorButtonToScreen( screen ) | |
if Editor.button then | |
Editor.button:addToScreen(screen) | |
screen:on("touched",Editor.toggleVisible) | |
end | |
end | |
function Editor.toggleVisible(t) | |
if t.state == ENDED and t.tapCount == 3 then | |
Editor.setVisible(Editor, not Editor.visible ) | |
end | |
end | |
-- the editor tab modifies the Button Class too: | |
function Button:listenToEditor() | |
if Editor.button and self.save then -- setting self=true makes the buttons not editable | |
Editor:on("editorOn", self.setEditorOn, self) | |
Editor:on("editorOff", self.setEditorOff, self) | |
end | |
end | |
function Button:setEditorOn() | |
self.oldDrag = self.onDrag | |
self.onDrag = self.dragMe | |
self.oldTouchable = self.touchable | |
self:setTouchable(true) | |
self.oldRectVisible = self.rectVisible | |
self:setRectVisible(true) | |
end | |
function Button:setEditorOff() | |
self.onDrag = self.oldDrag | |
if self.oldTouchable~=nil then self:setTouchable( self.oldTouchable ) end | |
if self.oldRectVisible~=nil then self:setRectVisible( self.oldRectVisible ) end | |
end | |
function Editor:setVisible(bool) | |
self.visible = bool | |
if self.visible then | |
self.button:setVisible(true) | |
self.button:setTouchable(true) | |
else | |
self.button:setVisible(false) | |
self.button:setTouchable(false) | |
self.savePending = false -- turning invisible cancell the changes | |
self:setState(false) | |
end | |
end | |
-- this toggles the editor active / not active | |
function Editor:setState(bool) | |
self.state = bool | |
if self.state then | |
self:trigger("editorOn") | |
self:setStateButtonOn() | |
self.savePending = true | |
else | |
self:trigger("editorOff") | |
if self.savePending then | |
self.savePending = false | |
self:setStateButtonWait() | |
tween.delay(0.1, function()saver:save() end) | |
tween.delay(1, function() self:setStateButtonOff() end) | |
else | |
self:setStateButtonOff() | |
end | |
end | |
end | |
function Editor:setStateButtonOn() | |
self.button.txt:setText("editor is on") | |
self.button.rect:setStyle({fill = color(200,0,0) }) | |
end | |
function Editor:setStateButtonWait() | |
self.button.txt:setText("saving...") | |
end | |
function Editor:setStateButtonOff() | |
self.button.txt:setText("editor is off") | |
self.button.rect:setStyle({fill = color(0,0,0,64) }) | |
end | |
--# screen1 | |
-- screen1 | |
createScreen1 = function() | |
screen1 = Screen() | |
local screen = screen1 | |
local b = backgroundButton( screen, color(30, 31, 102, 255)) | |
Editor.addEditorButtonToScreen( screen ) | |
tutoTitle( screen, "button 1.1", "Welcome to Xfc 3.0!") | |
tutoSubTitle( screen, "button 1.3", "Author: JMV38 - July 2014") | |
tutoTextCenter( screen, "button 1.2",[[ | |
Xfc 3.0 is a library of buttons for Codea | |
This is a tutorial for using it | |
Swipe left to go to next screen | |
Swipe right to go back to previous screen | |
]]) | |
end | |
tutoColor1 = color(103, 31, 31, 255) | |
tutoColor2 = color(103, 31, 31, 255) | |
function tutoTitle(screen, name, txt) | |
noWrapButton( screen, name,300, 50, {fontSize=50}, txt) | |
end | |
function tutoSubTitle(screen, name, txt) | |
noWrapButton( screen, name,300, 50, {fontSize=30, font= "Arial-ItalicMT"}, txt) | |
end | |
function tutoTextLeft(screen, name, txt) | |
longTextButton( screen, name,WIDTH, 300, {fontSize=20, font="ArialMT", textAlign=LEFT}, txt) | |
end | |
function tutoTextCenter(screen, name, txt) | |
longTextButton( screen, name,WIDTH, 300, {fontSize=20, font="ArialMT", textAlign=CENTER}, txt) | |
end | |
--# screen2 | |
-- screen2 | |
createScreen2 = function() | |
-- first step: create a screen to put your buttons into: | |
screen2 = Screen() | |
-- then add buttons. Lets start with 2 usefull (but optionnal) special buttons: | |
-- First a background button: | |
backgroundButton(screen2, color(103, 31, 31, 255)) | |
-- this button sets the color of the backround and listen to screen touched events. | |
-- this is the button that will change the screen when you swipe left or right. | |
-- the next button is special one to help edit your buttons (we'll see how later) | |
Editor.addEditorButtonToScreen( screen2 ) | |
-- after this you can add your own buttons | |
tutorial2(screen2) | |
end | |
-- tutorial part | |
tutorial2 = function(screen) | |
tutoTitle( screen, "button 2.1", "screen2") | |
tutoSubTitle( screen, "button 2.2", "Setting up a screen") | |
tutoTextLeft( screen, "button 2.3", [[ | |
-- first step: create a screen to put your buttons into: | |
screen2 = Screen() | |
-- then add buttons. Lets start with 2 usefull (but optionnal) special buttons: | |
-- First a background button: | |
backgroundButton(screen2, color(103, 31, 31, 255)) | |
-- this button sets the color of the backround and listen to screen touched events. | |
-- this is the button that will change the screen when you swipe left or right. | |
-- the next button is special one to help edit your buttons (we'll see how later) | |
Editor.addEditorButtonToScreen( screen2 ) | |
-- after this you can add your own buttons | |
(look at tab 'screen2' to get this code) | |
swipe left to continue... | |
]]) | |
end | |
--# screen3 | |
-- screen3 | |
createScreen3 = function() | |
screen3 = Screen({ backgroundColor = color(30, 31, 102, 255) }) | |
local screen = screen3 | |
backgroundButton( screen, color(30, 31, 102, 255)) | |
Editor.addEditorButtonToScreen( screen ) | |
local name = "myButton 1" -- each button must have a unique name to be easily editable | |
local x,y,w,h = WIDTH/2-100, HEIGHT-400, 200, 50 -- define button size and position | |
local b = Button(name) -- this creates the button | |
b:addToScreen(screen) -- this adds the button to screen (a button can go to several screens) | |
tutorial3(screen) | |
end | |
-- tutorial part | |
tutorial3 = function(screen) | |
tutoTitle( screen, "button 3.1", "screen3") | |
tutoSubTitle( screen, "button 3.2", "adding a button to the screen") | |
tutoTextLeft( screen, "button 3.3", [[ | |
To create and view a button, you only need 2 lines: | |
local b = Button("myButton 1") -- each button must have a unique name to be easily editable | |
b:addToScreen(screen) -- this adds the button to screen (a button can go to several screens) | |
Here is the result: | |
]]) | |
tutoTextLeft( screen, "button 3.5", [[ | |
Now let's see how to put the button where you want: | |
- triple tap on the screen background to show up the editor button. Editor is off. | |
- tap the editor button to turn it on. Now the touchable region of each button shows up. | |
- drag your button where you want it | |
- tap the editor button to turn it off. After 1s the buttons are saved in the tab 'data'. | |
- triple tap on the screen background to hide the editor button | |
]]) | |
tutoTextLeft( screen, "button 3.4", | |
[[(look at tab 'screen3' to get this code) swipe left to continue...]]) | |
end | |
--# screen4 | |
-- screen4 | |
createScreen4 = function() | |
screen4 = Screen() | |
local screen = screen4 | |
backgroundButton( screen, tutoColor1) | |
Editor.addEditorButtonToScreen( screen ) | |
-- define button position directly | |
local x,y = 50, HEIGHT-300 | |
local b = Button("myButton 2",x,y) | |
b:setTxtVisible(false) -- hide text | |
b:addToScreen(screen) | |
-- define button size and position | |
local x,y,w,h = nil,nil, 220, 40 | |
local b = Button("myButton 3",x,y,w,h) | |
b:setRectVisible(false) -- hide rectangle | |
b:addToScreen(screen) | |
-- define rect style | |
local b = Button("myButton 4") | |
b.rect:setStyle({fill=color(0,128,0), strokeWidth=5, stroke=color(255,255,0), smooth=noSmooth, radius=20}) | |
b:addToScreen(screen) | |
-- define text and text style | |
local b = Button("myButton 5",nil,nil,120,70) | |
b.txt:setText("new text for myButton 5") | |
b.txt:setStyle({fill=color(0),font="ArialMT",fontSize=20, textMode=CENTER, textWrapWidth=110, textAlign=LEFT}) | |
b.rect:setStyle({fill=color(192), strokeWidth=0, radius=1}) | |
b:addToScreen(screen) | |
-- example | |
local b = Button("myButton 6",nil,nil,60,60) | |
b.txt:setText("6") | |
b.txt:setStyle({fontSize=40}) | |
b.rect:setStyle({fill=color(0), strokeWidth=1, radius=30}) | |
b:addToScreen(screen) | |
-- example | |
local b = Button("myButton 7",nil,nil,60,60) | |
b.txt:setText("7") | |
b.txt:setStyle({fontSize=40}) | |
b.rect:setStyle({fill=color(0), strokeWidth=1, radius=2}) | |
b:addToScreen(screen) | |
-- example | |
local b = Button("myButton 8",nil,nil,60,60) | |
b:setTxtVisible(false) | |
b.img:setStyle({innerMargin = 5}) | |
b.img:load("Cargo Bot:Command Right") | |
b.rect:setStyle({strokeWidth=2, radius=5}) | |
b:addToScreen(screen) | |
-- example | |
local b = Button("myButton 9",nil,nil,100,100) | |
b.txt:setText("9") | |
b:setRectVisible(false) | |
b.img:load( "Cargo Bot:Codea Icon") | |
b:addToScreen(screen) | |
-- define button not editable by setting its name to nil | |
local x,y = 50, 150 | |
local b = Button(nil,x,y) | |
b.txt:setText("not editable") | |
b:addToScreen(screen) | |
-- a generic button to prove all touch functions work | |
function testTouch(screen, func) | |
local b = Button("test_"..func) | |
b.txt:setText(func) | |
b.txt:setStyle({font="ArialMT",fontSize=20}) | |
b.onEnter = b.doNothing | |
b.onLeave = b.doNothing | |
b[func] = function(self,t) | |
if not self.waiting then | |
Button.onEnter(self,t) | |
self.waiting = true | |
tween.delay(1, function() | |
Button.onLeave(self) | |
self.waiting = false | |
end) | |
end | |
end | |
b:addToScreen(screen) | |
end | |
testTouch(screen, "onBegan") | |
testTouch(screen, "onMoving") | |
testTouch(screen, "onEnded") | |
testTouch(screen, "onTap") | |
testTouch(screen, "onDrag") | |
testTouch(screen, "onDrop") | |
testTouch(screen, "onEnter") | |
testTouch(screen, "onLeave") | |
testTouch(screen, "onSwipeLeft") | |
testTouch(screen, "onSwipeRight") | |
testTouch(screen, "onSwipeUp") | |
testTouch(screen, "onSwipeDown") | |
tutorial4(screen) | |
end | |
-- tutorial part | |
tutorial4 = function(screen) | |
tutoTitle( screen, "button 4.1", "screen4") | |
tutoSubTitle( screen, "button 4.2", "changing button properties") | |
tutoTextLeft( screen, "button 4.3", [[ | |
you can set the button aspect: | |
]]) | |
tutoTextCenter( screen, "button 4.5", | |
[[The button b will react to action X if the function b:onX(touch) is defined. | |
Example: doing the described action turns the button to blue]] | |
) | |
tutoTextLeft( screen, "button 4.4", | |
[[(look at tab 'screen4' to get this code) swipe left to continue...]]) | |
end | |
--# screen5 | |
createScreen5 = function() | |
screen5 = Screen() | |
local screen = screen5 | |
local b = backgroundButton( screen, color(30, 31, 102, 255)) | |
Editor.addEditorButtonToScreen( screen ) | |
tutoTitle( screen, "button 5.1", "Credits") | |
--tutoSubTitle( screen, "button 5.3", "Author: JMV38 - July 2014") | |
tutoTextCenter( screen, "button 5.2",[[ | |
The low level classes are from CargoBot. | |
The panel and screen concept are inspired from cargoBot too. | |
The eventMngr class is the result of a work from @tnlogy & @JMV38 & @Briarfox | |
The concepts of 'is a', 'has a', inheritance and mixin were very useful to me | |
in making this code manageable. Thanks to @Toadkick, @hyrovitaliprotago, | |
@hpsoft, @luatee, @SkyTheCoder and others on the forum for their help and support! | |
]]) | |
end | |
--# Main | |
-- Main.lua | |
DEV_MODE = false | |
supportedOrientations(LANDSCAPE_ANY) | |
function setup() | |
displayMode(FULLSCREEN) | |
-- displayMode(OVERLAY) | |
Editor:init() | |
createScreen1() | |
createScreen2() | |
createScreen3() | |
createScreen4() | |
createScreen5() | |
screen4:open() | |
-- screen1:setStyle({transitionComming = "fromLeft"}) | |
end | |
function draw() | |
Screen:draw() | |
local c = CurrentTouch | |
if c and c.state~=ENDED then | |
resetMatrix() | |
fill(255) | |
ellipse(c.x,c.y,50) | |
end | |
end | |
function touched(t) | |
Screen:touched(t) | |
end | |
--# Notes | |
-- High level notes on the code | |
-- =============== Class Hierarchy ================= | |
-- The first class to know about is Screen. It represents an ipad screen on which we | |
-- can draw stuff. It's main bit of functionality is that it knows how to handle meshes | |
-- and z-order of objects within these meshes. The current implementation just keeps a | |
-- different mesh for each texture in each z-order but obviously could be optimized to | |
-- for example uses an atlas | |
-- The various games screens are subclasses of Screen: Level, PackSelect, LevelSelect, | |
-- StartScreen, WinScreen. currentScreen is a global variable that corresponds to | |
-- the screen that is currently showing | |
-- Screen subclasses Panel. Panels are simple container objects that hold a bunch of | |
-- elems and recursively pass on methods to their elements. For example, put a bunch of | |
-- objects in a panel and by translating the panel you will translate all the elements | |
-- as well | |
-- Panels can also contain other other panels of course. So while screens are panels, many | |
-- of the objects on a screen are panels themselves. Also note that objects can | |
-- be added to the screen with add(), which is a panel method, but that doesn't mean that | |
-- they will be drawn (say an invisible button). | |
-- You could also have objects that are drawn but not added to the screen, for example, a | |
-- drawing that doesn't respond to any type of event ever doesn't necessarily have to be | |
-- add()'ed to the screen (but I guess no harm if it was added) | |
-- The leaves of the panel hierarchy are SpriteObjs. SpriteObjs are first created which | |
-- just their coordinates but as soon as they are added to a screen with doDraw, they become | |
-- part of that screen and will contain references to the meshes of that screen. From then on | |
-- we can set properties of SpriteObjs like position, tint, size and this will automatically | |
-- get reflected on the screen meshes. Functionality for moving a spriteObj from one screen | |
-- to another doesn't exist at the moment. | |
-- Two important subclasses of SpriteObj: Button and ShadowObj | |
-- Button is a SpriteObj that knows how to handle touches. Touches on a screen (or panels more | |
-- generally) are passed on to its elems, which can implement the touched method themselves. | |
-- Buttons do that - its touched metho checks if | |
-- the touch happened within the boundaries of the button, and forwards the touch to one of | |
-- its virtual methods: onBegan, onMoving, onEnded. | |
-- ShadowObj are SpriteObjs which have an internal SpriteObj to represent shadows. It then | |
-- overwrites all the SpriteObj methods to also apply them to the shadow. | |
-- The shadowOffset method determines the offset of the shadow relative to the object, given | |
-- its position. This method can be overwritten if we want to give the impression that the | |
-- shadow is elsewhere, for example the stage shadow works differently than the startScreen | |
-- shadow | |
-- ============= Level configuations ============== | |
-- The global variables packs and levels specify the parms for each level/pack | |
-- ============= Stage ============== | |
-- stages are defined in BaseStage/Stage/Goal and contain things like Piles,Claws,Crate | |
-- Different types of stage are drawn with different dimensions and sprites, and | |
-- so we define a config table which contains all the information needed to draw that stage | |
-- ============= Events ============== | |
-- Events are bound and triggered using the Events class, which is a static class | |
-- Note the interaction between the Panel class and Events. When a Panel is bound | |
-- it calls bindEvents on each of its elems so for each class you have to write | |
-- all binding of events into a bindEvents method. This also makes sure that the events | |
-- are easy to find and are not mixed with the rest of the code | |
--# data | |
saver.data = { | |
['editor'] = { x = 780.5, y = 681, }, | |
['button 1.1'] = { y = 584.5, x = 483.5, }, | |
['button 1.3'] = { y = 517, x = 480.5, }, | |
['button 1.2'] = { y = 333, x = 479, }, | |
['button 2.1'] = { y = 644, x = 472.5, }, | |
['button 2.2'] = { y = 581, x = 475.5, }, | |
['button 2.3'] = { y = 284.5, x = 508.5, }, | |
['myButton 1'] = { y = 352, x = 398, }, | |
['button 3.1'] = { y = 658.5, x = 470, }, | |
['button 3.2'] = { y = 590.5, x = 471.5, }, | |
['button 3.3'] = { y = 472, x = 484, }, | |
['button 3.5'] = { y = 204.5, x = 442.5, }, | |
['button 3.4'] = { y = 56, x = 493, }, | |
['myButton 2'] = { y = 464.5, x = 114.5, }, | |
['myButton 3'] = { y = 400.5, x = 36, }, | |
['myButton 4'] = { y = 321, x = 54, }, | |
['myButton 5'] = { y = 232.5, x = 28, }, | |
['myButton 6'] = { y = 232.5, x = 187, }, | |
['myButton 7'] = { y = 152.5, x = 277.5, }, | |
['myButton 8'] = { y = 375, x = 296, }, | |
['myButton 9'] = { y = 237, x = 270.5, }, | |
['test_onBegan'] = { y = 485, x = 440, }, | |
['test_onMoving'] = { y = 422, x = 439.5, }, | |
['test_onEnded'] = { y = 362.5, x = 440, }, | |
['test_onTap'] = { y = 303.5, x = 442, }, | |
['test_onDrag'] = { y = 481, x = 669.5, }, | |
['test_onDrop'] = { y = 420.5, x = 674, }, | |
['test_onEnter'] = { x = 673, y = 362.5, }, | |
['test_onLeave'] = { y = 303.5, x = 674, }, | |
['test_onSwipeLeft'] = { y = 225, x = 441, }, | |
['test_onSwipeRight'] = { y = 159.5, x = 443.5, }, | |
['test_onSwipeUp'] = { y = 154.5, x = 677.5, }, | |
['test_onSwipeDown'] = { y = 229, x = 677, }, | |
['button 4.1'] = { y = 690, x = 502, }, | |
['button 4.2'] = { y = 627.5, x = 504, }, | |
['button 4.3'] = { y = 537, x = 142.5, }, | |
['button 4.5'] = { y = 551.5, x = 654.5, }, | |
['button 4.4'] = { y = 71, x = 472, }, | |
['button 5.1'] = { y = 619.5, x = 490, }, | |
['button 5.2'] = { y = 351.5, x = 506, }, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment