Skip to content

Instantly share code, notes, and snippets.

@HyroVitalyProtago
Last active September 6, 2015 13:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HyroVitalyProtago/055093e0ec4f511e8fb6 to your computer and use it in GitHub Desktop.
Save HyroVitalyProtago/055093e0ec4f511e8fb6 to your computer and use it in GitHub Desktop.
Btn is a simple and lite button library for Codea
automatically created by Required (0.0.28) from HyroVitalyProtago
hyrovitalyprotago@gmail.com
https://gist.github.com/7802bd00e7d56d480379
return (function()
local Btn = class()
-- todo
-- Advanced
-- size = "lg" | "sm" | "xs" | "default",
-- defined by width | height | width,height | fontSize | width,height,fontSize
-- width => fontSize max + height auto
-- height => fontSize max + width auto
-- width,height => fontSize max
-- fontSize => width,height auto
-- width,height,fontSize => width,height max, favorite fontSize (if too large => adjusted)
local styles = {
primary = {
background = color(39, 78, 191),
fill = color(220),
active = {
background = color(19, 58, 171)
}
},
success = {
background = color(80, 210, 97),
fill = color(220),
active = {
background = color(60, 190, 77),
}
},
info = {
background = color(79, 175, 208),
fill = color(220),
active = {
background = color(59, 155, 188),
}
},
warning = {
background = color(207, 147, 40),
fill = color(220),
active = {
background = color(187, 127, 20),
}
},
danger = {
background = color(180, 74, 57),
fill = color(220),
active = {
background = color(160, 54, 37),
}
},
link = {
noBackground = true,
noStroke = true,
fill = color(46, 108, 152),
active = {
fill = color(6, 48, 92),
}
}
}
local default = setmetatable({
mode = CENTER,
font = "HelveticaNeue-Light",
fontSize = 32,
fill = color(20),
background = color(220),
noBackground = false,
stroke = color(20),
noStroke = false,
strokeWidth = 2,
textWrapWidth = 0,
active = {
background = color(200)
},
shape = "rect",
x = 0,
y = 0,
w = 200,
h = 50,
angle = 0, -- rotation
padding = {
h = 20, -- horizontal
v = 5 -- vertical
},
onPress = function(self, touch) end,
onRelease = function(self, touch) end
}, {
__call = function(default,a,k)
return a[k] or (a.style and styles[a.style][k]) or (a.inherit and type(_G[k]) == "function" and _G[k]()) or default[k]
end
})
local shapes = {
rect = rect,
circle = ellipse
}
local function backgroundContext(self)
stroke(self.stroke)
if self.noStroke then noStroke() else strokeWidth(self.strokeWidth) end
if self.noBackground then noFill() else fill(self.background) end
end
local function textContext(self)
font(self.font)
fontSize(self.fontSize)
fill(self.fill)
textWrapWidth(self.textWrapWidth)
end
local function getWidthAndHeightFromFontSize(self)
pushStyle()
textContext(self)
local w, h = textSize(self.text)
popStyle()
return w, h
end
local function adaptFontSize(self, valid) -- todo better
local w, h
local optimalFontSize = 0
repeat
pushStyle()
self.fontSize = optimalFontSize
textContext(self)
w, h = textSize(self.text)
popStyle()
optimalFontSize = optimalFontSize + 1
until not valid(optimalFontSize,w,h)
return w,h
end
local function draw(self)
backgroundContext(self)
rectMode(CENTER)
self.fshape(0, 0, self.w, self.h)
textContext(self)
text(self.text)
end
local function texture(s)
local t = image(s.w, s.h)
setContext(t)
pushMatrix()
translate(s.w*.5, s.h*.5)
pushStyle()
draw(s)
popStyle()
popMatrix()
setContext()
return t
end
local function meshBuild(w,h,t)
local m = mesh()
m:addRect(0,0,w,h)
m.texture = t
return m
end
local function decor(o,t)
return setmetatable(table.deepcopy(t),{
__index = function(tbl,k)
return o[k]
end
})
end
local function convertCoord(x,s)
return (math.type(x) == "float" and x*s) or (x < 0 and s-x) or x
end
function Btn:updateSize(args)
local h,v = self.padding.h*2, self.padding.v*2
if args.w or args.h then
self.w = args.w or self.w
self.h = args.h or self.h
local nw, nh = adaptFontSize(self, function(ft,w,h)
return w <= (args.w and args.w - h or w) and
h <= (args.h and args.h - v or h) and
ft <= (args.fontSize or ft)
end)
self.w = not args.w and nw + h or self.w
self.h = not args.h and nh + v or self.h
else
self.w, self.h = getWidthAndHeightFromFontSize(self)
self.w = self.w + h
self.h = self.h + v
end
if self.shape == "circle" then
self.w = math.max(self.w, self.h)
self.h = self.w
end
self:updatePos()
self:updateMesh()
end
function Btn:updatePos()
self.realX, self.realY = convertCoord(self.x, WIDTH), convertCoord(self.y, HEIGHT)
local tmp = vec2(self.w*.5, self.h*.5)
self.ct = vec2(self.realX, self.realY) + (self.mode == CORNER and tmp or vec2(0,0)) -- center
self.bl = self.ct - tmp -- bottomLeft
self.tr = self.ct + tmp -- topRight
end
function Btn:updateMesh()
self.textures = {
default = texture(self),
active = texture(decor(self, self.active))
}
self.mesh = meshBuild(self.w,self.h,self.textures[self.state])
end
function Btn:init(txt, args) -- inherit
assert(txt and type(txt) == "string", "[Btn] first arg need to be a string")
args = args or {}
self.text = txt
self.state = "default" -- "default" or "active"
for k,_ in pairs(default) do
self[k] = default(args, k)
end
self.fshape = shapes[self.shape] or _G[default.shape] -- inherited
if self.shape == "circle" then
self.inBounds = function(self, p) -- override in bounds for circle
return self.ct:dist(vec2(p.x, p.y)) < self.w*.5
end
end
self:updateSize(args) -- compute size
-- automaticaly called by updateSize
-- self:updatePos() -- pos: absolute, screen (width, height)
-- self:updateMesh()
end
function Btn:draw()
pushMatrix()
translate(self.ct.x, self.ct.y)
rotate(self.angle)
self.mesh:draw()
popMatrix()
end
function Btn:inBounds(p)
return p.x >= self.bl.x and p.x <= self.tr.x and p.y >= self.bl.y and p.y <= self.tr.y
end
function Btn:touched(t)
local touch = (vec2(t.x, t.y) - self.ct):rotate(-self.angle) + self.ct
if self:inBounds(touch) then
if not self:isActive() then self:onIn() end
if t.state == BEGAN then
self:pressed(touch)
elseif t.state == ENDED then
self:released(touch)
end
elseif self:isActive() then
self:onOut()
end
end
function Btn:changeState(state)
self.state = state
self.mesh.texture = self.textures[state] -- update texture
end
function Btn:clone()
return Btn(self.text, {}) -- todo
end
function Btn:isActive() return self.state == "active" end
function Btn:onIn() self:changeState("active") end
function Btn:onOut() self:changeState("default") end
function Btn:pressed(t) self:onPress(t) end
function Btn:released(t) self:onOut() self:onRelease(t) end
function Btn:orientationChanged(o) print("orientationChanged", o) end
return Btn
end)
return (function()
local BtnGrp = class()
--[[ todo
size = "lg" | "sm" | "xs" | "default",
mode = CENTER, (UP, LEFT, RIGHT, BOTTOM, ...)
background = ...,
margin = 5, -- space between buttons
]]--
local function convertCoord(x,s)
return (math.type(x) == "float" and x*s) or (x < 0 and s-x) or x
end
function BtnGrp:init(group, args)
assert(group and type(group) == "table", "[BtnGrp] first arg need to be a table")
args = args or {}
self.group = group
self.align = args.align or "vertical"
self.realX, self.realY = convertCoord(args.x or .5, WIDTH), convertCoord(args.y or .5, HEIGHT)
local maxWidth, maxHeight = 0, 0
-- move buttons to the right place
local x, y = self.realX, self.realY
for _,btn in ipairs(self.group) do
maxWidth, maxHeight = math.max(maxWidth, btn.w), math.max(maxHeight, btn.h)
btn.x, btn.y = math.floor(x), math.floor(y) -- todo setPos
btn:updatePos()
x, y = x + (self.align == "horizontal" and btn.w or 0), y + (self.align == "vertical" and btn.h or 0)
end
-- right size based on bigger size
local newSize = { w = maxWidth, h = maxHeight }
for _,btn in ipairs(self.group) do
btn:updateSize(newSize) -- todo setSize
btn:updateMesh()
end
end
function BtnGrp:get(i) end -- todo
function BtnGrp:insert(btn) end -- todo
function BtnGrp:remove(btn) end -- todo
function BtnGrp:draw()
for _,btn in ipairs(self.group) do
btn:draw()
end
end
function BtnGrp:touched(t)
for _,btn in ipairs(self.group) do
btn:touched(t)
end
end
function BtnGrp:orientationChanged(o)
for _,btn in ipairs(self.group) do
btn:orientationChanged(o)
end
end
return BtnGrp
end)
-- Btn
local btns, map = {}
function setup()
displayMode(FULLSCREEN)
required.path 'Btn'
local Btn = required.import 'Btn'
local BtnGrp = required.import 'BtnGrp'
local grp = {}
local styles = {"Default", "Primary", "Success", "Info", "Warning", "Danger", "Link"}
for _,v in pairs(styles) do
grp[#grp+1] = Btn(v, {
style = v ~= "Default" and v:lower()
})
end
btns[#btns+1] = BtnGrp(grp, {
x = .85,
y = .3
})
local next_pos = (function(step)
local i = step
return function()
local tmp = i
i = i + step
return tmp
end
end)
local npl = next_pos(1/4)
-- Circle
btns[#btns+1] = Btn("GO", {
x = .15,
y = npl(),
shape = "circle",
background = color(0),
fill = color(220),
fontSize = 50,
active = {
background = color(255),
fill = color(0)
}
})
-- Circle with utf-8 icon
btns[#btns+1] = Btn("\u{2654}", {
x = .15,
y = npl(),
shape = "circle",
background = color(0),
fill = color(220),
fontSize = 80,
active = {
background = color(255),
fill = color(0)
}
})
-- Rect with utf-8 icon
btns[#btns+1] = Btn("\u{2663}", {
x = .15,
y = npl(),
fontSize = 80,
padding = { h=15, v=7 },
active = {
background = color(255),
fill = color(0)
}
})
-- with image
--[[
btns[#btns+1] = Btn(readImage("Cargo Bot:Star Filled"), {
x = .15,
y = npl(),
shape = "circle",
background = color(0),
active = {
background = color(255),
fill = color(0)
}
})
]]--
local npl = next_pos(1/6)
-- width => fontSize max + height auto
btns[#btns+1] = Btn("Fixed width", {
x = .5,
y = npl(),
w = 100
})
-- height => fontSize max + width auto
btns[#btns+1] = Btn("Fixed height", {
x = .5,
y = npl(),
h = 75
})
-- width,height => fontSize max
btns[#btns+1] = Btn("Fixed width,height", {
x = .5,
y = npl(),
w = 400,
h = 50
})
-- fontSize => width,height auto
btns[#btns+1] = Btn("Fixed fontSize", {
x = .5,
y = npl(),
fontSize = 64
})
-- width,height,fontSize => width,height max, favorite fontSize (if too large => adjusted)
btns[#btns+1] = Btn("Fixed width,height,fontSize", {
x = .5,
y = npl(),
w = 400,
h = 80,
fontSize = 24
})
end
function draw()
background(200)
for _,v in pairs(btns) do v:draw() end
end
function touched(t)
for _,v in pairs(btns) do v:touched(t) end
end
function orientationChanged(o)
for _,v in pairs(btns) do v:orientationChanged(o) end
end
{
"author":"HyroVitalyProtago",
"id":"055093e0ec4f511e8fb6",
"description":"Btn is a simple and lite button library for Codea",
"version":"0.0.4",
"codea":"2.3.1",
"name":"Btn"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment