Skip to content

Instantly share code, notes, and snippets.

@tnlogy
Created January 26, 2013 20:33
Show Gist options
  • Save tnlogy/4644451 to your computer and use it in GitHub Desktop.
Save tnlogy/4644451 to your computer and use it in GitHub Desktop.
QML implementation attempt for Codea
--# UI
UI = {}
function UI.C(code)
return loadstring("return " .. code)
end
UI.Item = class()
UI.Item.defaults = {
id="id",
x=0, y=0, w=100, h=100,
opacity=255, color=color(255, 255, 255, 255)
}
function UI.defaults(ds)
for k,v in pairs(UI.Item.defaults) do
if not ds[k] then ds[k] = v end
end
return ds
end
function UI.Item:setupAnchors(attr)
attr.right = function() return x+w end
attr.top = function() return y+h end
if not attr.anchors then return end
local anchorMethods = {
centerIn=function(c)
attr.x = c .. ".w/2"; attr.y = c .. ".h/2"
end,
fill=function(c)
attr.x = c .. ".x"; attr.y = c .. ".y"
attr.w = c .. ".w"; attr.h = c .. ".h"
end
}
for k,v in pairs(attr.anchors) do
anchorMethods[k](v)
end
attr.anchors = nil
end
function UI.Item:callWithEnv(f)
local env = {}
local cenv = getfenv(f)
setmetatable(env, {
__index = function (t, k)
local v = self.attr[k]
if v ~= nil then return v end
v = self.idMap[k]
if v ~= nil then return v.attr end
return cenv[k]
end
})
setfenv(f, env)
return f()
end
function UI.Item:init(attr, ...)
local klass = getmetatable(self)
self.rawAttr = attr
self:setupAnchors(attr)
for k,v in pairs(attr) do
local v = attr[k]
if type(v) == "string" and
klass.defaults[k] ~= nil and
type(klass.defaults[k]) ~= "string" then
v = UI.C(v)
attr[k] = v
end
end
self.attr = {}
self.idMap = {}
setmetatable(self.attr, {
__index = function(t, k)
local cv = self.preattr and self.preattr[k]
if cv ~= nil then return cv end
local v = attr[k]
if v == nil then
cv = self.postattr and self.postattr[k]
if cv ~= nil then return cv end
return klass.defaults[k]
elseif type(v) == "function" then
return self:callWithEnv(v)
else
return v
end
end,
__newindex = function (t,k,v)
attr[k] = v
end
})
if attr.id then
self.idMap[attr.id] = self
end
self.children = arg
for i,c in ipairs(self.children) do
c.attr.parent = self.attr
for k,v in pairs(c.idMap) do self.idMap[k] = v end
setmetatable(c.idMap, {__index=self.idMap})
if i > 1 then c.idMap.prev = arg[i-1] end
if i+1 < #arg then c.idMap.next = arg[i+1] end
end
getmetatable(self).__call = function(...) return self.new(...) end
end
function UI.Item:id(id)
local x = self.idMap[id]
return x and x.attr
end
function UI.Item:draw()
if self.attr.opacity == 0 then return end
for i,c in ipairs(self.children) do
if c.attr.opacity ~= 0 then
pushMatrix()
translate(c.attr.x, c.attr.y)
c:draw()
resetStyle()
popMatrix()
end
end
end
function UI.Item:touched(touch)
for i,c in ipairs(self.children) do
pushMatrix()
translate(c.attr.x, c.attr.y)
c:touched(touch)
resetStyle()
popMatrix()
end
end
function UI.Item:new(attr, ...)
attr.source = self:clone()
return UI.Loader(attr, ...)
end
function UI.Item:clone()
local cs = {}
for i,c in ipairs(self.children) do
table.insert(cs, c:clone())
end
local klass = getmetatable(self)
local attr = {}
for k,v in pairs(self.rawAttr) do
if attr[k] == nil then attr[k] = v end
end
return klass(attr, unpack(cs))
end
UI.Rect = class(UI.Item)
function UI.Rect:draw()
fill(self.attr.color)
rect(0,0, self.attr.w, self.attr.h)
UI.Item.draw(self)
end
UI.Image = class(UI.Item)
UI.Image.defaults = UI.defaults({w=300, h=300,image="SpaceCute:Star"})
function UI.Image:draw()
spriteMode(CORNER)
tint(255,255,255,self.attr.opacity)
sprite(self.attr.image, 0, 0, self.attr.w, self.attr.h)
end
UI.Text = class(UI.Item)
UI.Text.defaults = UI.defaults({text="Text"})
function UI.Text:draw()
fill(self.attr.color)
text(self.attr.text)
end
UI.Loader = class(UI.Item)
function UI.Loader:init(attr, ...)
UI.Item.init(self, attr, ...)
local c = self.attr.source
c.attr.parent = self.attr
c.preattr = self.attr
self.postattr = {}
setmetatable(self.postattr, {
__index = function(t, k)
local v = c.rawAttr[k]
if type(v) == "function" then
return c:callWithEnv(v)
else return v end
end
})
end
function UI.Loader:draw()
self.attr.source:draw()
end
function UI.Loader:touched(touch)
local component = self.attr.source
component:touched(touch)
end
UI.TouchArea = class(UI.Item)
UI.TouchArea.defaults = UI.defaults({
pressed=false,
onPressed=function() end,
onClicked=function() end
})
function UI.TouchArea:touched(touch)
local m = modelMatrix()
local p = vec2(m[13], m[14])
local p2 = vec2(p.x+self.attr.w, p.y+self.attr.h)
local inside = false
if touch.x >= p.x and touch.x <= p2.x and
touch.y >= p.y and touch.y <= p2.y then
inside = true
end
local pressed = touch.state == BEGAN or touch.state == MOVING
if touch.state == ENDED and inside and self.attr.onClicked then
self.attr.onClicked()
end
self.attr.pressed = pressed and inside
-- local s = math.sqrt(m[1]*m[1]+m[2]*m[2]+m[3]*m[3])
-- local r = math.atan2(m[2], m[1])
if inside and self.attr.onPressed and pressed then
self.attr.onPressed(touch)
end
end
--# Main
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"}})
)
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment