Created
May 20, 2013 10:41
-
-
Save loopspace/5611552 to your computer and use it in GitHub Desktop.
Library UI Release v2.3 -A library of classes and functions for user interface.
This file contains hidden or 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
Library UI Tab Order | |
------------------------------ | |
This file should not be included in the Codea project. | |
#ChangeLog | |
#Main | |
#NumberSpinner | |
#CubicSelector | |
#PictureBrowser | |
#Keyboard | |
#Keypad | |
#Menu | |
#Slider | |
#UI | |
#PalmRest | |
#FontPicker | |
#ListSelector |
This file contains hidden or 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
--[[ | |
ChangeLog | |
======== | |
v2.3 Picturebrowser runs on start-up | |
v2.2 Latest version of autogist | |
v2.1 Updated to use latest version of cmodule. Cleaned up Main | |
v2.0 Split into separate projects: "Library UI" contains the user | |
interface elements. | |
v1.0 Converted to use toadkick's cmodule for importing | |
and Briarfox's AutoGist for versionning. | |
It needs toadkick's code from | |
https://gist.github.com/apendley/5411561 | |
tested with version 0.0.8 | |
To use without AutoGist, comment out the lines in setup. | |
--]] |
This file contains hidden or 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
--[==[ | |
-- Cubic | |
local Cubic = class() | |
local cubicShader | |
function Cubic:init(...) | |
self.scale = 200 | |
self.x = WIDTH/2 | |
self.y = HEIGHT/2 | |
self.colour = color(161, 140, 24, 255) | |
self.bgcolour = color(61, 29, 29, 255) | |
self.guidecolour = color(28, 101, 139, 129) | |
local m = mesh() | |
local nsteps = 200 | |
local w = 5 | |
local h = 1 | |
for n=1,nsteps do | |
m:addRect(0,(n-.5)*h,w,h) | |
end | |
local s = shader() | |
s.vertexProgram,s.fragmentProgram = cubicShader() | |
m.shader = s | |
m.shader.len = nsteps | |
self.mesh = m | |
self:setCoefficients(...) | |
end | |
function Cubic:setCoefficients(...) | |
if arg.n == 4 then | |
self.coefficients = vec4(arg[1],arg[2],arg[3],arg[4]) | |
elseif arg.n == 1 then | |
if type(arg[1]) == "table" then | |
self.coefficients = arg[1].coefficients | |
else | |
self.coefficients = arg[1] | |
end | |
end | |
self.coefficients = self.coefficients or vec4(0,1,0,0) | |
self:toBezier() | |
self:update() | |
end | |
function Cubic:update() | |
self.mesh.shader.pts_1 = self.coefficients.x | |
self.mesh.shader.pts_2 = self.coefficients.y | |
self.mesh.shader.pts_3 = self.coefficients.z | |
self.mesh.shader.pts_4 = self.coefficients.w | |
end | |
function Cubic:toBezier() | |
self.bezier = vec4( | |
self.coefficients.x, | |
self.coefficients.y/3, | |
self.coefficients.x + 2*self.coefficients.y/3 | |
+ self.coefficients.z/3, | |
self.coefficients.x + self.coefficients.y | |
+ self.coefficients.z + self.coefficients.w | |
) | |
end | |
function Cubic:fromBezier() | |
self.coefficients = vec4( | |
self.bezier.x, | |
3*(self.bezier.y - self.bezier.x), | |
3*(self.bezier.x - 2*self.bezier.y + self.bezier.z), | |
-self.bezier.x + 3*self.bezier.y - 3*self.bezier.z + self.bezier.w | |
) | |
self:update() | |
end | |
function Cubic:draw() | |
if not self.active then | |
return | |
end | |
pushMatrix() | |
resetMatrix() | |
pushStyle() | |
resetStyle() | |
translate(self.x - self.scale/2,self.y - self.scale/2) | |
scale(self.scale) | |
fill(self.bgcolour) | |
RoundedRectangle(-.2,-.2,1.4,1.4,.2) | |
strokeWidth(5/self.scale) | |
lineCapMode(SQUARE) | |
stroke(0, 0, 0, 255) | |
line(0,0,0,1) | |
line(0,1,1,1) | |
line(1,1,1,0) | |
line(1,0,0,0) | |
self.mesh:setColors(self.colour) | |
self.mesh:draw() | |
stroke(self.guidecolour) | |
line(0,self.bezier.x,1/3,self.bezier.y) | |
line(1/3,self.bezier.y,2/3,self.bezier.z) | |
line(2/3,self.bezier.z,1,self.bezier.w) | |
popStyle() | |
popMatrix() | |
end | |
function Cubic:isTouchedBy(touch) | |
return self.active | |
end | |
function Cubic:processTouches(g) | |
if g.updated then | |
if g.num == 1 then | |
local t = g.touchesArr[1] | |
local v = vec2(t.touch.x,t.touch.y) | |
v = v - vec2(self.x - self.scale/2,self.y - self.scale/2) | |
v = v / self.scale | |
if t.touch.state == BEGAN then | |
if v.x < 1/6 then | |
self.moving = "x" | |
elseif v.x < 1/2 then | |
self.moving = "y" | |
elseif v.x < 5/6 then | |
self.moving = "z" | |
else | |
self.moving = "w" | |
end | |
elseif g.type.moved then | |
self.bezier[self.moving] = v.y | |
self:fromBezier() | |
if self.ctscallback then | |
self.ctscallback(self.coefficients) | |
end | |
end | |
elseif g.type.tap and g.type.ended then | |
if self.callback then | |
if self.callback(self.coefficients) then | |
self:deactivate() | |
end | |
end | |
elseif g.type.moved then | |
local v,n = vec2(0,0),0 | |
for _,t in ipairs(g.touchesArr) do | |
if t.updated then | |
v = v + vec2(t.touch.deltaX,t.touch.deltaY) | |
n = n + 1 | |
end | |
end | |
if n > 0 then | |
v = v / n | |
self.x = self.x + v.x | |
self.y = self.y + v.y | |
end | |
end | |
g:noted() | |
end | |
if g.type.ended and not g.type.tap then | |
g:reset() | |
end | |
end | |
function Cubic:activate(v,f,ff,b) | |
self.active = true | |
self.callback = f | |
self.ctscallback = ff | |
if v then | |
if b then | |
self.bezier = v | |
self:fromBezier() | |
else | |
self.coefficients = v | |
self:toBezier() | |
self:update() | |
end | |
end | |
end | |
function Cubic:deactivate() | |
self.active = false | |
self.callback = nil | |
self.ctscallback = nil | |
end | |
if testsuite then | |
testsuite.addTest({ | |
name = "Cubic", | |
setup = function() | |
local w = Cubic(1,2,3,4) | |
print(w.coefficients) | |
local z = Cubic(vec4(2,3,4,5)) | |
print(z.coefficients) | |
end, | |
draw = function() | |
end | |
}) | |
end | |
cubicShader = function() | |
return [[ | |
// | |
// A basic vertex shader | |
// | |
//This is the current model * view * projection matrix | |
// Codea sets it automatically | |
uniform mat4 modelViewProjection; | |
//This is the current mesh vertex position, color and tex coord | |
// Set automatically | |
attribute vec4 position; | |
attribute vec4 color; | |
attribute vec2 texCoord; | |
//This is an output variable that will be passed to the fragment shader | |
varying lowp vec4 vColor; | |
varying highp vec2 vTexCoord; | |
uniform float pts_1; | |
uniform float pts_2; | |
uniform float pts_3; | |
uniform float pts_4; | |
uniform float len; | |
void main() | |
{ | |
highp float t = position.y/len; | |
highp float y = pts_1 + t*pts_2 + t*t*pts_3 + t*t*t*pts_4; | |
highp float dy = pts_2 + 2.0*t*pts_3 + 3.*t*t*pts_4; | |
highp vec2 bdir = vec2(dy,-1.); | |
bdir = position.x*normalize(bdir)/len; | |
highp vec2 bpos = vec2(t,y) + bdir; | |
highp vec4 bzpos = vec4(bpos.x,bpos.y,0,1); | |
//Pass the mesh color to the fragment shader | |
vColor = color; | |
vTexCoord = texCoord; | |
//Multiply the vertex position by our combined transform | |
gl_Position = modelViewProjection * bzpos; | |
} | |
]],[[ | |
// | |
// A basic fragment shader | |
// | |
//This represents the current texture on the mesh | |
uniform lowp sampler2D texture; | |
//The interpolated vertex color for this fragment | |
varying lowp vec4 vColor; | |
//The interpolated texture coordinate for this fragment | |
varying highp vec2 vTexCoord; | |
void main() | |
{ | |
//Sample the texture at the interpolated coordinate | |
lowp vec4 col = vColor; | |
if (vTexCoord.x < .2) | |
col.a = col.a*vTexCoord.x/.2; | |
if (vTexCoord.x > .8) | |
col.a = col.a*(1.-vTexCoord.x)/.2; | |
//Set the output color to the texture color | |
gl_FragColor = col; | |
} | |
]] | |
end | |
return Cubic | |
--]==] |
This file contains hidden or 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
--[==[ | |
-- FontPicker | |
local ListSelector = cimport "ListSelector" | |
local Font,Sentence = unpack(cimport "Font") | |
local FontPicker = class() | |
FontPicker.Fonts = { | |
{"Academy Engraved", "AcademyEngravedLetPlain"}, | |
{"American Typewriter","AmericanTypewriter", | |
{ | |
{"Light", "AmericanTypewriter-Light"}, | |
{"Regular", "AmericanTypewriter"}, | |
{"Bold", "AmericanTypewriter-Bold"}, | |
{"Condensed Light", "AmericanTypewriter-CondensedLight"}, | |
{"Condensed", "AmericanTypewriter-Condensed"}, | |
{"Condensed Bold", "AmericanTypewriter-CondensedBold"}, | |
} | |
}, | |
{"Arial","ArialMT", | |
{ | |
{"Regular","ArialMT"}, | |
{"Italic","Arial-ItalicMT"}, | |
{"Bold","Arial-BoldMT"}, | |
{"Bold Italic","Arial-BoldItalicMT"}, | |
} | |
}, | |
{"Baskerville","Baskerville", | |
{ | |
{"Regular","Baskerville"}, | |
{"Italic","Baskerville-Italic"}, | |
{"SemiBold","Baskerville-SemiBold"}, | |
{"SemiBold Italic","Baskerville-SemiBoldItalic"}, | |
{"Bold","Baskerville-Bold"}, | |
{"Bold Italic","Baskerville-BoldItalic"}, | |
} | |
}, | |
{"Bodoni IT","BodoniSvtyTwoITCTT-Book", | |
{ | |
{"Regular","BodoniSvtyTwoITCTT-Book"}, | |
{"Italic","BodoniSvtyTwoITCTT-BookIta"}, | |
{"Bold","BodoniSvtyTwoITCTT-Bold"}, | |
{"Small Caps","BodoniSvtyTwoSCITCTT-Book"}, | |
} | |
}, | |
{"Bodoni OSIT","BodoniSvtyTwoOSITCTT-Book", | |
{ | |
{"Regular","BodoniSvtyTwoOSITCTT-Book"}, | |
{"Italic","BodoniSvtyTwoOSITCTT-BookIta"}, | |
{"Bold","BodoniSvtyTwoOSITCTT-Bold"}, | |
} | |
}, | |
--{"Bodoni Ornaments","BodoniOrnamentsITCTT"}, | |
{"Bradley Hand","BradleyHandITCTT-Bold"}, | |
{"Chalkboard","ChalkboardSE-Light", | |
{ | |
{"Regular","ChalkboardSE-Regular"}, | |
{"Bold","ChalkboardSE-Bold"}, | |
} | |
}, | |
{"Chalkduster","Chalkduster"}, | |
{"Copperplate","Copperplate", | |
{ | |
{"Light","Copperplate-Light"}, | |
{"Regular","Copperplate"}, | |
{"Bold","Copperplate-Bold"}, | |
} | |
}, | |
{"Courier","Courier", | |
{ | |
{"Regular","Courier"}, | |
{"Oblique","Courier-Oblique"}, | |
{"Bold","Courier-Bold"}, | |
{"Bold Oblique","Courier-BoldOblique"}, | |
} | |
}, | |
{"Courier New","CourierNewPSMT", | |
{ | |
{"Regular","CourierNewPSMT"}, | |
{"Bold","CourierNewPS-BoldMT"}, | |
{"Bold Italic","CourierNewPS-BoldItalicMT"}, | |
{"Italic","CourierNewPS-ItalicMT"}, | |
} | |
}, | |
-- {"DBLCDTempBlack","DBLCDTempBlack"}, | |
{"Didot","Didot", | |
{ | |
{"Regular","Didot"}, | |
{"Italic","Didot-Italic"}, | |
{"Bold","Didot-Bold"}, | |
} | |
}, | |
{"Futura","Futura-Medium", | |
{ | |
{"Medium","Futura-Medium"}, | |
{"Medium Italic","Futura-MediumItalic"}, | |
{"Condensed Medium","Futura-CondensedMedium"}, | |
{"Condensed Extra Bold","Futura-CondensedExtraBold"}, | |
} | |
}, | |
{"Georgia","Georgia", | |
{ | |
{"Regular","Georgia"}, | |
{"Italic","Georgia-Italic"}, | |
{"Bold","Georgia-Bold"}, | |
{"Bold Italic","Georgia-BoldItalic"}, | |
} | |
}, | |
{"Gill Sans", "GillSans", | |
{ | |
{"Light","GillSans-Light"}, | |
{"Light Italic","GillSans-LightItalic"}, | |
{"Regular","GillSans"}, | |
{"Italic","GillSans-Italic"}, | |
{"Bold","GillSans-Bold"}, | |
{"Bold Italic","GillSans-BoldItalic"}, | |
} | |
}, | |
{"Helvetica","Helvetica", | |
{ | |
{"Light","Helvetica-Light"}, | |
{"Light Oblique", "Helvetica-LightOblique"}, | |
{"Regular","Helvetica"}, | |
{"Oblique","Helvetica-Oblique"}, | |
{"Bold","Helvetica-Bold"}, | |
{"Bold Oblique","Helvetica-BoldOblique"}, | |
} | |
}, | |
{"Helvetica Neue","HelveticaNeue", | |
{ | |
{"Ultra Light","HelveticaNeue-UltraLight"}, | |
{"Ultra Light Italic", "HelveticaNeue-UltraLightItalic"}, | |
{"Light","HelveticaNeue-Light"}, | |
{"Light Italic","HelveticaNeue-LightItalic"}, | |
{"Regular","HelveticaNeue"}, | |
{"Italic","HelveticaNeue-Italic"}, | |
{"Medium","HelveticaNeue-Medium"}, | |
{"Bold","HelveticaNeue-Bold"}, | |
{"Bold Italic","HelveticaNeue-BoldItalic"}, | |
{"Condensed Bold","HelveticaNeue-CondensedBold"}, | |
{"Condensed Black","HelveticaNeue-CondensedBlack"}, | |
} | |
}, | |
{"Hoefler","HoeflerText-Regular", | |
{ | |
{"Regular","HoeflerText-Regular"}, | |
{"Italic","HoeflerText-Italic"}, | |
{"Bold","HoeflerText-Black"}, | |
{"Bold Italic","HoeflerText-BlackItalic"}, | |
} | |
}, | |
{"Inconsolata","Inconsolata"}, | |
{"Marion","Marion-Regular", | |
{ | |
{"Regular","Marion-Regular"}, | |
{"Italic","Marion-Italic"}, | |
{"Bold","Marion-Bold"}, | |
} | |
}, | |
{"MarkerFelt","MarkerFelt-Thin", | |
{ | |
{"Thin","MarkerFelt-Thin"}, | |
{"Wide","MarkerFelt-Wide"}, | |
} | |
}, | |
{"Noteworthy","Noteworthy-Light", | |
{ | |
{"Light","Noteworthy-Light"}, | |
{"Bold","Noteworthy-Bold"}, | |
} | |
}, | |
{"Optima","Optima-Regular", | |
{ | |
{"Italic","Optima-Italic"}, | |
{"Regular","Optima-Regular"}, | |
{"Bold","Optima-Bold"}, | |
{"Bold Italic","Optima-BoldItalic"}, | |
{"Extra Bold","Optima-ExtraBlack"}, | |
} | |
}, | |
{"Palatino","Palatino-Roman", | |
{ | |
{"Regular","Palatino-Roman"}, | |
{"Italic","Palatino-Italic"}, | |
{"Bold","Palatino-Bold"}, | |
{"Condensed","Palatino-BoldItalic"}, | |
} | |
}, | |
{"Papyrus","Papyrus", | |
{ | |
{"Regular","Papyrus"}, | |
{"Condensed","Papyrus-Condensed"}, | |
} | |
}, | |
{"PartyLetPlain","PartyLetPlain"}, | |
{"Snell Roundhand","SnellRoundhand", | |
{ | |
{"Regular","SnellRoundhand"}, | |
{"Bold","SnellRoundhand-Bold"}, | |
{"Black","SnellRoundhand-Black"}, | |
} | |
}, | |
{"Times New Roman","TimesNewRomanPSMT", | |
{ | |
{"Regular","TimesNewRomanPSMT"}, | |
{"Italic","TimesNewRomanPS-ItalicMT"}, | |
{"Bold","TimesNewRomanPS-BoldMT"}, | |
{"Bold Italic","TimesNewRomanPS-BoldItalicMT"}, | |
} | |
}, | |
{"Trebuchet","TrebuchetMS", | |
{ | |
{"Regular","TrebuchetMS"}, | |
{"Italic","TrebuchetMS-Italic"}, | |
{"Bold","TrebuchetMS-Bold"}, | |
{"Bold Italic","Trebuchet-BoldItalic"}, | |
} | |
}, | |
{"Verdana","Verdana", | |
{ | |
{"Regular","Verdana"}, | |
{"Italic","Verdana-Italic"}, | |
{"Bold","Verdana-Bold"}, | |
{"Bold Italic","Verdana-BoldItalic"}, | |
} | |
}, | |
-- "ZapfDingbatsITC", -- Interesting chars not in range 0-255 | |
--]] | |
--{"Zapfino","Zapfino"} | |
--]] | |
} | |
function FontPicker:init() | |
end | |
function FontPicker:activate(t) | |
t = t or {} | |
self.args = t | |
local l = {} | |
local f = t.fonts or self.Fonts | |
t.fonts = nil | |
self.fonts = f | |
for k,v in ipairs(f) do | |
table.insert(l, | |
Sentence(Font({name = v[2], size = 24}), v[1])) | |
end | |
t.list = l | |
self.action = t.action | |
t.action = nil | |
self.list = ListSelector(t) | |
t.list = nil | |
self.active = true | |
self.list:activate({ | |
action = function(n) return self:processFont(n) end | |
}) | |
end | |
function FontPicker:deactivate() | |
self.active = false | |
self.action = nil | |
self.list:deactivate() | |
self.list = nil | |
end | |
function FontPicker:draw() | |
if self.active then | |
self.list:draw() | |
end | |
end | |
function FontPicker:isTouchedBy(touch) | |
if self.active and self.list then | |
return self.list:isTouchedBy(touch) | |
end | |
return false | |
end | |
function FontPicker:processTouches(g) | |
return self.list:processTouches(g) | |
end | |
function FontPicker:processFont(n) | |
if self.fonts[n][3] then | |
local t = self.args | |
t.fonts = self.fonts[n][3] | |
t.action = self.action | |
self:activate(t) | |
else | |
if self.action(self.fonts[n][2]) then | |
self:deactivate() | |
return true | |
end | |
end | |
return false | |
end | |
return FontPicker | |
--]==] |
This file contains hidden or 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
--[==[ | |
-- Keyboard for text input | |
-- Author: Andrew Stacey | |
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html | |
-- Licence: CC0 http://wiki.creativecommons.org/CC0 | |
local Keyboard = class() | |
local Colour = unpack(cimport "Colour",nil) | |
local UTF8 = cimport "utf8" | |
local Font,Sentence = unpack(cimport "Font") | |
cimport "ColourNames" | |
cimport "RoundedRectangle" | |
--[[ | |
These global constants define our lock states: | |
"None": lower case characters | |
"Cap": next character is capitalised, afterwards revert to "None" | |
"Shift": upper case characters | |
"Num": the numerical value of each key is returned. | |
--]] | |
local LOCK_NONE = 1 | |
local LOCK_CAP = 2 | |
local LOCK_SHIFT = 3 | |
local LOCK_NUM = 4 | |
function Keyboard:advanceLock() | |
if self.lock == LOCK_CAP then | |
if self.locktime and ElapsedTime - self.locktime < 1 then | |
self.lock = LOCK_SHIFT | |
else | |
self.lock = LOCK_NONE | |
end | |
elseif self.lock == LOCK_NONE then | |
self.lock = LOCK_CAP | |
self.locktime = ElapsedTime | |
else | |
self.lock = LOCK_NONE | |
end | |
end | |
Keyboard.qwerty = { | |
{"q","w","e","r","t","y","u","i","o","p"}, | |
{{nil,nil,nil,nil,.5},"a","s","d","f","g","h","j","k","l"}, | |
{"","z","x","c","v","b","n","m"} | |
} | |
Keyboard.fullqwerty = { | |
{"q","w","e","r","t","y","u","i","o","p",{"DEL",BACKSPACE}}, | |
{{nil,nil,nil,nil,.5},"a","s","d","f","g","h","j","k","l", | |
{Sentence(Font({ | |
name = "Zapfino", size="100" | |
}),"CR"),"\n",nil,nil,1.5}}, | |
{{"¥",Keyboard.advanceLock,"£",Keyboard.advanceLock}, | |
"z","x","c","v","b","n","m",{",",nil,"!"},{".",nil,"?"}}, | |
{"","","",{" "," ",nil,nil,6}} | |
} | |
function Keyboard:init(t) | |
self.fontname = t.name | |
self.fonttype = t.fonttype | |
self.type = t.type | |
self.keys = {} | |
local rl = 0 | |
local nr = 0 | |
self.deactivated = {} | |
self.keys = {} | |
local cc | |
for i,r in ipairs(Keyboard[self.type]) do | |
local rrl = 0 | |
for j,k in ipairs(r) do | |
rrl = rrl + 1 | |
end | |
rl = math.max(rl,rrl) | |
nr = nr + 1 | |
self.deactivated[i] = {} | |
end | |
self.rowlength = rl | |
self.numrows = nr | |
self.activekey = {} | |
self.active = false | |
self.resize = true | |
self.colour = Colour.svg.LightGray | |
self.pcolour = Colour.svg.DarkGray | |
self.dcolour = Colour.svg.DarkSlateGray | |
self.kcolour = Colour.svg.Black | |
self:initialise(t.width) | |
end | |
function Keyboard:initialise(w) | |
w = w or WIDTH | |
self.iwidth = w | |
self.keywidth = math.floor(w/self.rowlength) | |
self.width = self.keywidth * self.rowlength | |
self.padding = 10 | |
local rl = self.keywidth - 2*self.padding | |
local f | |
if self.fonttype == "bitmap" then | |
local fs = {64,48,32,24,12} | |
for k,v in ipairs(fs) do | |
if (v - rl) < 0 then | |
self.fontsize = v | |
break | |
end | |
end | |
f = Font[self.fontname .. " " .. self.fontsize .. "0"]() | |
else | |
rl = math.floor(rl) | |
self.fontsize = rl | |
f = Font({ | |
name = self.fontname, | |
size = rl | |
}) | |
end | |
self.font = f | |
local row,ccc,CCC,l,key | |
for i,r in ipairs(Keyboard[self.type]) do | |
self.keys[i] = {} | |
for j,k in ipairs(r) do | |
if type(k) == "string" then | |
if k ~= "" then | |
key = UTF8(k) | |
ccc = Sentence(f,key) | |
ccc:setColour(self.kcolour) | |
ccc:prepare() | |
key:toupper() | |
CCC = Sentence(f,key) | |
CCC:setColour(self.kcolour) | |
CCC:prepare() | |
self.keys[i][j] = { | |
ccc,nil,CCC,nil,1 | |
} | |
else | |
self.keys[i][j] = { | |
nil,nil,nil,nil,1 | |
} | |
end | |
else | |
if k[1] then | |
ccc = Sentence(f,k[1],{"size"}) | |
ccc:setColour(self.kcolour) | |
ccc:prepare() | |
if k[3] then | |
CCC = Sentence(f,k[3],{"size"}) | |
else | |
CCC = Sentence(f,ccc,{"size"}) | |
CCC:toupper() | |
end | |
CCC:setColour(self.kcolour) | |
CCC:prepare() | |
l = k[5] or 1 | |
self.keys[i][j] = { | |
ccc,k[2],CCC,k[4],l | |
} | |
else | |
l = k[5] or 1 | |
self.keys[i][j] = { | |
nil,nil,nil,nil,l | |
} | |
end | |
end | |
end | |
end | |
self.lh = f:lineheight() | |
self.keyheight = self.lh + 4*self.padding | |
local kh = self.keyheight | |
--[[ | |
local kw = self.keywidth | |
local kr = self.padding | |
local image = image(kw,kh) | |
local hw = math.ceil(kw/2) | |
local hh = math.ceil(kh/2) | |
local a,d | |
for i = 1,hw do | |
for j = 1,hh do | |
if i < kr and j < kr then | |
d = math.sqrt((kr - i)^2 + (kr - j)^2) | |
elseif i < kr then | |
d = kr - i | |
elseif j < kr then | |
d = kr - j | |
else | |
d = 0 | |
end | |
if d > kr then | |
a = 0 | |
elseif d > kr - 4 then | |
a = 255*.25*(kr - d) | |
else | |
a = 255 | |
end | |
image:set(i,j,255,255,255,a) | |
image:set(kw-i,j,255,255,255,a) | |
image:set(i,kh-j,255,255,255,a) | |
image:set(kw-i,kh-j,255,255,255,a) | |
end | |
end | |
self.keypad = image | |
--]] | |
self.exh = self.font:exh() | |
--[[ | |
local xx,yy,xy = 0,0,0 | |
yy = self.numrows * kh | |
self.height = yy | |
local row | |
--]] | |
self.height = self.numrows * kh | |
self.lock = LOCK_NONE | |
self.top = self.height | |
if displayMode() == FULLSCREEN then | |
self.top = self.top + 50 | |
end | |
end | |
function Keyboard:draw(x,y) | |
if not self.active then | |
return | |
end | |
x = x or 0 | |
if displayMode() == FULLSCREEN then | |
y = y or 50 | |
else | |
y = y or 0 | |
end | |
self.x = x | |
self.y = y | |
pushStyle() | |
spriteMode(CORNER) | |
local col,kh,kw,xx,yy,ex,kr | |
ex = self.exh/2 | |
kh = self.keyheight | |
kw = self.keywidth | |
kr = self.padding | |
yy = y + self.height | |
self.top = yy | |
for i,r in ipairs(self.keys) do | |
yy = yy - kh | |
xx = x | |
for j,k in ipairs(r) do | |
if k[1] then | |
if self.deactivated[i][j] then | |
col = self.dcolour | |
else | |
if i == self.activekey[1] | |
and j == self.activekey[2] | |
then | |
col = self.pcolour | |
else | |
col = self.colour | |
end | |
end | |
fill(col) | |
RoundedRectangle(xx+kr/8,yy+kr/8,k[5]*kw-kr/4,kh-kr/4,kr) | |
tint(Colour.svg.Black) | |
if self.lock == LOCK_NONE then | |
k[1]:draw(xx+k[5]*kw/2-k[1].width/2,yy+kh/2 - ex) | |
else | |
k[3]:draw(xx+k[5]*kw/2-k[3].width/2,yy+kh/2 - ex) | |
end | |
end | |
xx = xx + k[5]*kw | |
end | |
end | |
popStyle() | |
end | |
function Keyboard:isTouchedBy(touch) | |
if not self.active then | |
return false | |
end | |
if touch.x < self.x then | |
return false | |
end | |
if touch.x > self.x + self.width then | |
return false | |
end | |
if touch.y < self.y then | |
return false | |
end | |
if touch.y > self.y + self.height then | |
return false | |
end | |
return true | |
end | |
function Keyboard:processTouches(g) | |
if g.updated then | |
if g.num == 1 then | |
local t = g.touchesArr[1] | |
local x,y,r,c | |
x = t.touch.x - self.x | |
y = self.height - t.touch.y + self.y | |
r = math.floor(y/self.keyheight) + 1 | |
c = x/self.keywidth | |
for k,v in ipairs(self.keys[r]) do | |
if c < v[5] then | |
c = k | |
break | |
else | |
c = c - v[5] | |
end | |
end | |
if not self.deactivated[r][c] | |
and self.keys[r] | |
and self.keys[r][c] | |
and self.keys[r][c][1] | |
then | |
self.activekey = {r,c} | |
end | |
end | |
g:noted() | |
end | |
if g.type.ended then | |
if self.activekey[1] then | |
local k = self.keys[self.activekey[1]][self.activekey[2]] | |
local kk | |
if self.lock == LOCK_NONE then | |
kk = k[2] or k[1]:getUTF8() | |
else | |
kk = k[4] or k[3]:getUTF8() | |
end | |
if type(kk) == "function" then | |
kk(self) | |
else | |
if self.callback and | |
self.callback(kk,self.activekey) then | |
self.active = false | |
end | |
if self.lock == LOCK_CAP then | |
self.lock = LOCK_NONE | |
end | |
end | |
self.activekey = {} | |
end | |
g:reset() | |
end | |
end | |
function Keyboard:activate(f) | |
self.active = true | |
self.callback = f | |
self:reactivateallkeys() | |
end | |
function Keyboard:deactivate() | |
self.active = false | |
self.callback = nil | |
self:reactivateallkeys() | |
end | |
function Keyboard:fullscreen(od,d) | |
if not self.resize then | |
return | |
end | |
if od == d then | |
return | |
end | |
local ow,w | |
if d == STANDARD then | |
w = 751 | |
else | |
w = 1024 | |
end | |
if od == STANDARD then | |
ow = 751 | |
else | |
ow = 1024 | |
end | |
self:initialise(self.iwidth * w/ow) | |
end | |
function Keyboard:deactivatekey(k) | |
self.deactivated[k[1]][k[2]] = true | |
end | |
function Keyboard:reactivatekey(k) | |
self.deactivated[k[1]][k[2]] = nil | |
end | |
function Keyboard:reactivateallkeys() | |
for k,v in ipairs(self.deactivated) do | |
self.deactivated[k] = {} | |
end | |
end | |
return Keyboard | |
--]==] |
This file contains hidden or 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
--[==[ | |
-- Keypad class for text input | |
-- Author: Andrew Stacey | |
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html | |
-- Licence: CC0 http://wiki.creativecommons.org/CC0 | |
--[[ | |
The "Keypad" class defines an object that behaves a little like the | |
input on a mobile phone. Via a call-back function, it can be used to | |
get a text string from the user for use in a program. | |
There are two types of Keypad: the phone type and a numeric type based | |
on the numeric pad on a keyboard. | |
--]] | |
local Keypad = class() | |
local Font, Sentence = unpack(cimport "Font",nil) | |
local UTF8 = cimport "utf8" | |
local Colour = unpack(cimport "Colour",nil) | |
cimport "ColourNames" | |
--[[ | |
These global constants define our lock states: | |
"None": lower case characters | |
"Cap": next character is capitalised, afterwards revert to "None" | |
"Shift": upper case characters | |
"Num": the numerical value of each key is returned. | |
--]] | |
local LOCK_NONE = 1 | |
local LOCK_CAP = 2 | |
local LOCK_SHIFT = 3 | |
local LOCK_NUM = 4 | |
local lock_sym = {} | |
--[[ | |
The locks are indicated on the display by certain strings which need | |
to be initialised. This needs the "Font" class to be loaded already | |
so we define a function to do the initialisation. | |
--]] | |
local function kp_set_locks(f,c) | |
lock_sym = { | |
Sentence(f,"abc"), | |
Sentence(f,"Abc"), | |
Sentence(f,"ABC"), | |
Sentence(f,"123") | |
} | |
for k,v in pairs(lock_sym) do | |
v:setColour(c) | |
end | |
end | |
--[[ | |
The standard keypad has certain features that need to be initialised. | |
These include: | |
The characters on the "control" keys. | |
The characters shown on the keys, both in normal or numeric mode. | |
The key lists which determine which characters are returned by each | |
key, both in normal or numeric mode. | |
--]] | |
local kp_left | |
local kp_ret | |
local kp_right | |
local kp_del | |
local kp_keystr | |
local kp_keylists | |
local kp_np_keystr | |
local kp_np_keylists | |
local kp_initialised | |
local function kp_initialise() | |
if kp_initialised then | |
return true | |
end | |
kp_left = UTF8(5589)--utf8hex("ab") | |
kp_ret = UTF8("Enter") | |
kp_right = UTF8(5586) | |
kp_del = UTF8("Del") | |
kp_keystr = { | |
{kp_left}, | |
{kp_ret}, | |
{kp_right}, | |
{"1"}, | |
{"2","ABC"}, | |
{"3","DEF"}, | |
{"4","GHI"}, | |
{"5","JKL"}, | |
{"6","MNO"}, | |
{"7","PQRS"}, | |
{"8","TUV"}, | |
{"9","WXYZ"}, | |
{kp_del}, | |
{"0"}, | |
{"^"} | |
} | |
kp_keylists = { | |
{14,{".",",","'","?","!","\"","1","-","(",")","@","/",":","_"},"1"}, | |
{12,{"a","b","c","2","ä","æ","å","à","á","â","ã","ç"},"2"}, | |
{8,{"d","e","f","3","è","é","ê","ë"},"3"}, | |
{8,{"g","h","i","4","ì","í","î","ï"},"4"}, | |
{5,{"j","k","l","5","£"},"5"}, | |
{11,{"m","n","o","6","ö","ø","ò","ó","ô","õ","ñ"},"6"}, | |
{7,{"p","q","r","s","7","ß","$"},"7"}, | |
{8,{"t","u","v","8","ù","ú","û","ü"},"8"}, | |
{5,{"w","x","y","z","9"},"9"}, | |
{1,{""},""}, | |
{2,{" ","0"},"0"} | |
} | |
kp_np_keystr = { | |
{kp_left}, | |
{kp_ret}, | |
{kp_right}, | |
{"7"}, | |
{"8"}, | |
{"9"}, | |
{"4"}, | |
{"5"}, | |
{"6"}, | |
{"1"}, | |
{"2"}, | |
{"3"}, | |
{kp_del}, | |
{"0"}, | |
{"."} | |
} | |
kp_np_keylists = { | |
{1,{},"7"}, | |
{1,{},"8"}, | |
{1,{},"9"}, | |
{1,{},"4"}, | |
{1,{},"5"}, | |
{1,{},"6"}, | |
{1,{},"1"}, | |
{1,{},"2"}, | |
{1,{},"3"}, | |
{1,{},""}, | |
{1,{},"0"}, | |
{1,{},"."} | |
} | |
kp_initialised = true | |
end | |
--[[ | |
This is our initialisation function which defines the coordinates of | |
the top left corner, the dimensions of a single "key pad", our colour, | |
and whether we are numeric or text. | |
--]] | |
function Keypad:init(t) | |
-- you can accept and set parameters here | |
t = t or {} | |
kp_initialise() | |
self.active = false | |
self.autoactive = true | |
self.opos = t.pos or function() return 0,0 end | |
self.anchor = t.anchor or "north west" | |
self.width = t.width | |
self.height = t.height | |
self.colour = t.colour | |
self.sep = 10 | |
self:orientationChanged() | |
self.font = t.font or Font({name = "Courier", size = 16}) | |
self.preStr = Sentence(self.font,"") | |
self.preStr:setColour(Colour.svg.Black) | |
self.postStr = Sentence(self.font,"") | |
self.postStr:setColour(Colour.svg.Black) | |
self.chr = Sentence(self.font,"") | |
self.chr:setColour(Colour.svg.Black) | |
self.cursor = Sentence(self.font,UTF8(124)) | |
self.cursor:setColour(Colour.svg.Black) | |
self.currchr = {} | |
if t.numeric then | |
self.keystr = kp_np_keystr | |
self.keylists = kp_np_keylists | |
self.keylists[10] = function() self:deleteChar() end | |
self.lock = LOCK_NUM | |
else | |
self.keystr = kp_keystr | |
self.keylists = kp_keylists | |
self.lock = LOCK_NONE | |
self.keylists[10] = function() self:deleteChar() end | |
self.keylists[12] = function() self:advanceLock() end | |
kp_set_locks(self.font,Colour.shade(self.colour,50)) | |
end | |
self.keys = {} | |
for k,v in ipairs(self.keystr) do | |
table.insert(self.keys,{}) | |
for l,u in ipairs(v) do | |
local s = Sentence(self.font,u) | |
s:setColour(Colour.svg.Black) | |
table.insert(self.keys[k],s) | |
end | |
end | |
end | |
function Keypad:orientationChanged() | |
local x,y = self.opos() | |
x,y = RectAnchorAt( | |
x, | |
y, | |
3 * (self.width + self.sep), | |
6 * (self.height + self.sep), | |
self.anchor) | |
y = y + 6 * (self.height + self.sep) | |
self.x = x | |
self.y = y | |
end | |
--[[ | |
This is our draw function which renders the keypad and its constituent | |
parts to the screen, assuming that we are active. | |
--]] | |
function Keypad:draw() | |
if self.active then | |
local k,x,y | |
pushStyle() | |
smooth() | |
noStroke() | |
ellipseMode(RADIUS) | |
fill(self.colour) | |
k = 0 | |
for j = 2,6 do | |
for i = 0,2 do | |
k = k + 1 | |
x = self.x + i * (self.width + self.sep) + self.sep/2 | |
y = self.y - j * (self.height + self.sep) + self.sep/2 | |
RoundedRectangle( | |
x, | |
y, | |
self.width, | |
self.height, | |
self.sep) | |
if self.keys[k] then | |
for l,u in ipairs(self.keys[k]) do | |
u:prepare() | |
u:draw( | |
x + self.width/2 - u.width/2, | |
y + self.height/2 - self.font:lineheight() * (l - 1) | |
) | |
end | |
end | |
end | |
end | |
x = self.x + self.sep/2 | |
y = self.y - self.sep/2 - self.height | |
RoundedRectangle( | |
x, | |
y, | |
3*self.width + 2*self.sep, | |
self.height, | |
self.sep) | |
self.preStr:prepare() | |
self.postStr:prepare() | |
self.chr:prepare() | |
x = x + self.sep | |
y = y + (self.height - self.font:lineheight())/2 | |
x,y = self.preStr:draw(x,y) | |
x,y = self.chr:draw(x,y) | |
if math.floor(2*ElapsedTime)%2 == 0 then | |
self.cursor:draw(x,y) | |
end | |
x = x + self.font:charWidth() | |
x,y = self.postStr:draw(x,y) | |
x = self.x + self.sep | |
y = self.y - self.sep/2 - self.font:lineheight() | |
lock_sym[self.lock]:draw(x,y) | |
popStyle() | |
end | |
end | |
--[[ | |
If we are active then we claim any touch that falls within our natural | |
"bounding box". If we're in "autoactive" mode then a touch outside our bounding box turns us off. | |
--]] | |
function Keypad:isTouchedBy(touch) | |
if self.active then | |
if touch.x < self.x | |
or touch.x > self.x + 3 * (self.width + self.sep) | |
or touch.y > self.y | |
or touch.y < self.y - 6 * (self.height + self.sep) then | |
if self.autoactive then | |
self:deactivate() | |
return true | |
else | |
return false | |
end | |
end | |
return true | |
end | |
end | |
--[[ | |
This routine process the touches. We process touches one by one as | |
they end. In normal mode, we have to keep track of the key that was | |
last pressed since if the same key is pressed within half a second | |
then we replace the previous character by the next on the list for | |
that key. | |
As we can move forwards and backwards in the string, we actually | |
maintain three Sentence objects: before the cursor, the current | |
character, and after the cursor. Moving the cursor shifts characters | |
back and forth between these three. | |
We also have to keep track of the current lock. | |
--]] | |
function Keypad:processTouches(g) | |
if self.active then | |
if g.updated then | |
local t,n,i,j | |
t = g.touchesArr | |
table.sort(t,sortByCreated) | |
for k,v in ipairs(t) do | |
if v.touch.state == ENDED then | |
j = math.floor((self.y - v.touch.y)/(self.height + self.sep)) + 1 | |
if j > 1 and j < 7 then | |
i = math.floor((v.touch.x - self.x)/(self.width + self.sep)) + 1 | |
if j == 2 then | |
self.preStr:append(self.chr) | |
self.chr:clear() | |
self.currchr = {} | |
if i == 2 then | |
self.preStr:append(self.postStr) | |
self.postStr:clear() | |
if self.returnFn then | |
local r | |
r = self.returnFn(self.preStr:getString()) | |
if r then | |
self:deactivate() | |
end | |
end | |
elseif i == 1 then | |
self.postStr:unshift(self.preStr:pop()) | |
elseif i == 3 then | |
self.preStr:push(self.postStr:shift()) | |
end | |
else | |
n = i + 3*(j - 3) | |
if n == self.currchr[1] | |
and v.createdat - self.currchr[4] < .5 then | |
self.currchr[2] = self.currchr[2] + 1 | |
self.currchr[4] = v.createdat | |
elseif type(self.keylists[n]) == "function" then | |
self.keylists[n]() | |
else | |
if self.lock == LOCK_CAP | |
and self.currchr[1] then | |
self.lock = LOCK_NONE | |
end | |
self.preStr:append(self.chr) | |
self.chr:clear() | |
self.currchr = {n,1,self.lock,v.updatedat} | |
end | |
end | |
v:destroy() | |
if self.currchr[1] then | |
local c | |
local a = self.keylists[self.currchr[1]] | |
if self.currchr[3] == LOCK_NUM then | |
c = UTF8(a[3]) | |
else | |
local d = (self.currchr[2] - 1) % a[1] +1 | |
c = UTF8(a[2][d]) | |
end | |
if self.currchr[3] == LOCK_CAP or | |
self.currchr[3] == LOCK_SHIFT then | |
c:toupper() | |
end | |
self.chr:setString(c) | |
end | |
end | |
end | |
end | |
end | |
g:noted() | |
else | |
g:reset() | |
end | |
end | |
--[[ | |
This functions shifts the lock to the next state, saving the current | |
character into the before-cursor string. | |
--]] | |
function Keypad:advanceLock() | |
self.preStr:append(self.chr) | |
self.chr:clear() | |
self.currchr = {} | |
self.lock = (self.lock) % 4 + 1 | |
end | |
--[[ | |
This deletes the current character (which may be the last one on the | |
before-cursor string). | |
--]] | |
function Keypad:deleteChar() | |
if self.currchr[1] then | |
self.chr:clear() | |
self.currchr = {} | |
else | |
self.preStr:pop() | |
end | |
end | |
--[[ | |
This activates the keypad, and sets the call-back function for when | |
the text input is finalised. | |
--]] | |
function Keypad:activate(f,x,y) | |
self:clear() | |
if x then | |
self.x = x | |
end | |
if y then | |
self.y = y | |
end | |
if f then | |
self.returnFn = f | |
end | |
self.active = true | |
end | |
function Keypad:deactivate() | |
self:clear() | |
if self.autoactive then | |
self.active = false | |
end | |
end | |
--[[ | |
This clears all the strings. | |
--]] | |
function Keypad:clear() | |
self.preStr:setString("") | |
self.postStr:setString("") | |
self.chr:setString("") | |
self.currchr = {} | |
self.returnFn = nil | |
end | |
--[[ | |
This is for sorting touches by their start time. | |
--]] | |
local function sortByCreated(a,b) | |
return a.createdat < b.createdat | |
end | |
Keypad.help = "The keypad and numeric pad are used to get (short) input from the user. The keypad acts like the input on a phone: multiple taps on the same key on quick succession cycle through the letters, and the case-key acts as on a phone. The numeric pad is similar to the numeric pad on a computer. For both, the upper keys are for left-right navigation through the input and to accept the input. The pad can be cancelled by touching some part of the screen away from it." | |
return Keypad | |
--]==] |
This file contains hidden or 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
--[==[ | |
-- List Selector | |
local Colour = unpack(cimport "Colour",nil) | |
cimport "ColourNames" | |
--cimport "Utilities" | |
local ListSelector = class() | |
function ListSelector:init(t) | |
t = t or {} | |
local list = t.list or {} | |
local mf = t.miniumSize or 10 | |
self.minsize = mf | |
self.choice = 1 | |
local w = 0 | |
local nf = 0 | |
local h = 0 | |
for k,s in ipairs(list) do | |
s:prepare() | |
w = math.max(w,s.width) | |
nf = nf + 1 | |
h = math.max(h, s.font:lineheight()) | |
end | |
w = w + 10 | |
self.totalwidth = w + 2 | |
self.nitems = nf | |
if nf > mf then | |
mf = nf | |
self.restrict = self.restrictMod | |
end | |
self.mitems = mf | |
self.pos = t.pos or function() return RectAnchorOf(Screen,"centre") end | |
self.ipos = self.pos | |
self.width = w | |
local hw = w/2 | |
self.m = mesh() | |
self.overlay = mesh() | |
local ver = {} | |
local col = {} | |
for k,v in ipairs({ | |
{0,0}, | |
{0,1}, | |
{1,1}, | |
{0,0}, | |
{1,0}, | |
{1,1}, | |
{0,0}, | |
{0,-1}, | |
{1,-1}, | |
{0,0}, | |
{1,0}, | |
{1,-1} | |
}) do | |
table.insert(ver,vec2(v[1],v[2])) | |
table.insert(col, | |
Colour.opacity(Colour.svg.Black,100*math.abs(v[2]))) | |
end | |
self.overlay.vertices = ver | |
self.overlay.colors = col | |
local img = image(2*w,h*math.ceil(mf/2)) | |
local x,y = 5,h*math.ceil(mf/2) | |
pushMatrix() | |
resetMatrix() | |
pushStyle() | |
resetStyle() | |
fill(Colour.svg.White) | |
noSmooth() | |
setContext(img) | |
rect(0,0,2*w,math.ceil(mf/2)*h) | |
for i=1,nf do | |
y = y + list[i].font.descent - h | |
list[i]:draw(x,y) | |
y = y - list[i].font.descent | |
if i == math.ceil(mf/2) then | |
x,y = w+5,h*math.ceil(mf/2) | |
end | |
end | |
setContext() | |
self.m.texture = img | |
self.img = img | |
local nv = 2*mf | |
local st = 2*math.pi/nv | |
ver = {} | |
local texc = {} | |
local a,b,c,d,tx,ty | |
local r = mf*h/(2*math.pi) | |
self.radius = r | |
a = 0 | |
b = -r | |
tx,ty = 0,0 | |
for i = 1,nv do | |
c = -r*math.sin(i*st) | |
d = -r*math.cos(i*st) | |
for k,v in ipairs({ | |
{-hw,a,b,0,2*(i-1)/nv}, | |
{hw,a,b,.5,2*(i-1)/nv}, | |
{hw,c,d,.5,2*i/nv}, | |
{-hw,a,b,0,2*(i-1)/nv}, | |
{-hw,c,d,0,2*i/nv}, | |
{hw,c,d,.5,2*i/nv} | |
}) do | |
table.insert(ver,vec3(v[1],v[2],v[3])) | |
table.insert(texc,vec2(tx+v[4],v[5]-ty)) | |
end | |
if i == mf then | |
tx,ty = .5,2*(i-1)/nv | |
end | |
a,b = c,d | |
end | |
self.m.vertices = ver | |
self.m.texCoords = texc | |
self.m:setColors(Colour.svg.White) | |
self.velocity = 0 | |
debug:log({name = "List: ", message = function() return self.choice end}) | |
end | |
function ListSelector:draw() | |
if self.active then | |
local ang | |
local w = self.totalwidth | |
local r = self.radius | |
local x,y = self.pos() | |
pushStyle() | |
pushMatrix() | |
resetMatrix() | |
resetStyle() | |
ortho(0,WIDTH,0,HEIGHT,2*r,-2*r) | |
translate(x,y,0) | |
camera(0,0,2*r,0,0,0,0,1,0) | |
local v = self.choice | |
ang = (v -.5)*360/self.mitems | |
pushMatrix() | |
rotate(ang,-1,0,0) | |
self.m:draw() | |
popMatrix() | |
if not self.intouch then | |
self.choice,self.velocity = self:restrict(v,self.velocity) | |
end | |
resetMatrix() | |
stroke(Colour.svg.SlateBlue) | |
strokeWidth(6) | |
noFill() | |
noSmooth() | |
rectMode(CORNERS) | |
pushMatrix() | |
translate(x-self.totalwidth/2-5,y) | |
scale(self.totalwidth +10,r+2) | |
self.overlay:draw() | |
popMatrix() | |
rect(x-self.totalwidth/2-5, | |
y-r-5, | |
x + self.totalwidth/2+5, | |
y+r+5) | |
popMatrix() | |
popStyle() | |
end | |
end | |
function ListSelector:activate(t) | |
t = t or {} | |
self.pos = t.pos or self.pos | |
self.choice = t.value or 1 | |
self.active = true | |
self.action = t.action or function() return true end | |
end | |
function ListSelector:deactivate() | |
self.pos = self.ipos | |
self.active = false | |
self.action = function() return true end | |
self.subfp = nil | |
end | |
function ListSelector:getValue() | |
local v = math.floor(self.choice+.5) | |
v = (v-1)%self.nitems + 1 | |
return v | |
end | |
function ListSelector:restrict(v,s) | |
if s == 0 then | |
if v ~= math.floor(v) then | |
local tgt = math.max(1, | |
math.min(self.nitems,math.floor(v + .5))) | |
if math.abs(v - tgt) < .01 then | |
v = tgt | |
else | |
v = v + DeltaTime*(tgt- v) | |
end | |
else | |
v = (v-1)%self.nitems + 1 | |
end | |
else | |
if v < -2 then | |
s = math.abs(s)/2 | |
end | |
if v > self.nitems + 2 then | |
s = - math.abs(s)/2 | |
end | |
v = v + DeltaTime*s | |
s = s * .99 | |
if math.abs(s) < .1 then | |
s = 0 | |
end | |
end | |
return v,s | |
end | |
function ListSelector:restrictMod(v,s) | |
if s == 0 then | |
if v ~= math.floor(v) then | |
local tgt = math.floor(v + .5) | |
if math.abs(v - tgt) < .01 then | |
v = tgt | |
else | |
v = v + DeltaTime*(tgt- v) | |
end | |
else | |
v = (v-1)%self.nitems + 1 | |
end | |
else | |
v = v + DeltaTime*s | |
s = s * .99 | |
if math.abs(s) < .1 then | |
s = 0 | |
end | |
end | |
return v,s | |
end | |
function ListSelector:isTouchedBy(touch) | |
if not self.active then | |
return false | |
end | |
local x,y = self.pos() | |
if touch.x < x - self.totalwidth/2 then | |
return false | |
end | |
if touch.x > x + self.totalwidth/2 then | |
return false | |
end | |
if touch.y < y - self.radius then | |
return false | |
end | |
if touch.y > y + self.radius then | |
return false | |
end | |
self.intouch = true | |
return true | |
end | |
function ListSelector:processTouches(g) | |
if g.type.ended and g.type.tap and g.num == 2 then | |
local value = self:getValue() | |
if self.action and self.action(value) then | |
self:deactivate() | |
end | |
g:reset() | |
return | |
end | |
local x,y = self.pos() | |
local t = g.touchesArr[1] | |
if t.touch.state ~= BEGAN then | |
if t.updated then | |
self.choice = (self.choice - 1 + | |
t.touch.deltaY/HEIGHT | |
*self.radius/self.mitems)%self.mitems | |
+ 1 | |
end | |
end | |
if t.touch.state == ENDED then | |
if t.short and t.moved then | |
self.velocity = 2*t:velocity().y/HEIGHT | |
*self.radius/self.mitems | |
else | |
self.velocity = 0 | |
end | |
end | |
g:noted() | |
if (not g.type.tap and g.type.ended) or g.type.finished then | |
self.intouch = false | |
g:reset() | |
end | |
end | |
ListSelector.help = "Drag the wheels up and down to change the digits. Drag the outermost wheels to add or remove digits." | |
return ListSelector | |
--]==] |
This file contains hidden or 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
VERSION = 2.3 | |
clearProjectData() | |
-- DEBUG = true | |
-- Use this function to perform your initial setup | |
function setup() | |
autogist = AutoGist("Library UI","A library of classes and functions for user interface.",VERSION) | |
autogist:backup(true) | |
--displayMode(FULLSCREEN_NO_BUTTONS) | |
cmodule "Library UI" | |
cmodule.path("Library Base", "Library Utilities") | |
local Touches = cimport "Touch" | |
local UI = cimport "UI" | |
local Debug = cimport "Debug" | |
local Colour = unpack(cimport "Colour",nil) | |
cimport "ColourNames" | |
cimport "PictureBrowser" | |
cimport "Menu" | |
cimport "Keypad" | |
cimport "Keyboard" | |
if cmodule.loaded("Menu") then | |
print("Menu loaded") | |
else | |
print("Menu not loaded") | |
end | |
touches = Touches() | |
ui = UI(touches) | |
debug = Debug({ui = ui}) | |
ui:systemmenu() | |
ui:helpmenu() | |
ui:addMessage("This is a system message, hello everyone.") | |
debug:log({ | |
name = "Screen north west", | |
message = function() local x,y = RectAnchorOf(Screen,"north west") return x .. ", " .. y end | |
}) | |
--debug:activate() | |
ui:setPictureList({directory = "Documents", camera = true, filter = function(n,w,h) return math.min(w,h) > 500 end}) | |
ui:getPicture(function(i) img = i return true end) | |
--print(USRotateCCW(vec2(1,0))) | |
ui:declareKeyboard({name = "ArialMT", type = "fullqwerty"}) | |
ui:useKeyboard("fullqwerty", | |
function(k) print(k) return false end) | |
--ui:getNumber(function(n) print(n) end) | |
end | |
-- This function gets called once every frame | |
function draw() | |
-- process touches and taps | |
touches:draw() | |
background(34, 47, 53, 255) | |
if img then | |
local w,h = img.width,img.height | |
local asp = math.min(WIDTH/w,HEIGHT/h,1) | |
sprite(img,WIDTH/2,HEIGHT/2) | |
end | |
ui:draw() | |
debug:draw() | |
touches:show() | |
end | |
function touched(touch) | |
touches:addTouch(touch) | |
end | |
function orientationChanged(o) | |
if ui then | |
ui:orientationChanged(o) | |
end | |
end | |
function fullscreen() | |
end | |
function reset() | |
end |
This file contains hidden or 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
--[==[ | |
--NumberSpinner | |
local NumberSpinner = class() | |
local Font = unpack(cimport "Font",nil) | |
local Colour = unpack(cimport "Colour",nil) | |
cimport "Coordinates" | |
cimport "Rectangle" | |
cimport "ColourNames" | |
function NumberSpinner:init(t) | |
t = t or {} | |
self.numbers = {0} | |
self.numdigits = 1 | |
self.decimals = {} | |
self.numdecs = 0 | |
self.maxdecs = 5 | |
self.maxdigits = 5 | |
self.allowneg = true | |
local fnt = t.font or Font({name = "Inconsolata", size = 60}) | |
self.pos = t.pos or function() return RectAnchorOf(Screen,"centre") end | |
self.ipos = self.pos | |
local h = fnt:lineheight() | |
local ds = fnt.descent | |
local w = fnt:charWidth("0") | |
fnt:setColour(Colour.svg.Black) | |
self.width = w | |
local hw = w/2 | |
self.m = mesh() | |
self.pm = mesh() | |
self.dpt = mesh() | |
self.overlay = mesh() | |
local ver = {} | |
local col = {} | |
for k,v in ipairs({ | |
{0,0}, | |
{0,1}, | |
{1,1}, | |
{0,0}, | |
{1,0}, | |
{1,1}, | |
{0,0}, | |
{0,-1}, | |
{1,-1}, | |
{0,0}, | |
{1,0}, | |
{1,-1} | |
}) do | |
table.insert(ver,vec2(v[1],v[2])) | |
table.insert(col, | |
Colour.opacity(Colour.svg.Black,100*math.abs(v[2]))) | |
end | |
self.overlay.vertices = ver | |
self.overlay.colors = col | |
local img = image(w,10*h) | |
pushMatrix() | |
resetMatrix() | |
pushStyle() | |
resetStyle() | |
fill(Colour.svg.White) | |
noSmooth() | |
setContext(img) | |
rect(0,0,w,10*h) | |
for i=0,9 do | |
fnt:write(i,0,i*h+ds) | |
end | |
setContext() | |
self.m.texture = img | |
img = image(w,10*h) | |
setContext(img) | |
rect(0,0,w,10*h) | |
fnt:write("-",0,4.5*h+ds) | |
setContext() | |
self.pm.texture = img | |
img = image(w,10*h) | |
setContext(img) | |
rect(0,0,w,10*h) | |
fnt:write(".",0,4.75*h+ds) | |
setContext() | |
popStyle() | |
popMatrix() | |
self.dpt.texture = img | |
local nv = 40 | |
local st = 2*math.pi/nv | |
ver = {} | |
local texc = {} | |
local a,b,c,d | |
local r = 5*h/math.pi | |
self.radius = r | |
a = 0 | |
b = -r | |
for i = 1,nv do | |
c = -r*math.sin(i*st) | |
d = -r*math.cos(i*st) | |
for k,v in ipairs({ | |
{-hw,a,b,0,(i-1)/nv}, | |
{hw,a,b,1,(i-1)/nv}, | |
{hw,c,d,1,i/nv}, | |
{-hw,a,b,0,(i-1)/nv}, | |
{-hw,c,d,0,i/nv}, | |
{hw,c,d,1,i/nv} | |
}) do | |
table.insert(ver,vec3(v[1],v[2],v[3])) | |
table.insert(texc,vec2(v[4],v[5])) | |
end | |
a,b = c,d | |
end | |
self.m.vertices = ver | |
self.m.texCoords = texc | |
self.m:setColors(Colour.svg.White) | |
self.pm.vertices = ver | |
self.pm.texCoords = texc | |
self.pm:setColors(Colour.svg.White) | |
self.dpt.vertices = ver | |
self.dpt.texCoords = texc | |
self.dpt:setColors(Colour.svg.White) | |
end | |
function NumberSpinner:draw() | |
if self.active then | |
local ang,tgt | |
local w = self.width +2 | |
local r = self.radius | |
local tw = 0 | |
local x,y = self.pos() | |
pushStyle() | |
pushMatrix() | |
resetMatrix() | |
resetStyle() | |
ortho(0,WIDTH,0,HEIGHT,2*r,-2*r) | |
translate(x+w/2,y,0) | |
camera(0,0,2*r,0,0,0,0,1,0) | |
for k,v in ipairs(self.numbers) do | |
ang = v*36 - 162 | |
tw = tw + w | |
translate(-w,0,0) | |
pushMatrix() | |
rotate(ang,1,0,0) | |
self.m:draw() | |
popMatrix() | |
if not self.intouch and v ~= math.floor(v) then | |
tgt = math.floor(v + .5) | |
if math.abs(v - tgt) < .01 then | |
self.numbers[k] = tgt | |
else | |
self.numbers[k] = v + DeltaTime*(tgt- v)*10 | |
end | |
end | |
end | |
self.numwidth = tw | |
if self.isneg then | |
translate(-w,0,0) | |
self.pm:draw() | |
tw = tw + w | |
end | |
self.totallwidth = tw | |
self.totalwidth = tw | |
self.decwidth = 0 | |
if self.numdecs > 0 then | |
translate(tw,0) | |
self.dpt:draw() | |
tw = w | |
for k,v in ipairs(self.decimals) do | |
ang = v*36 - 162 | |
tw = tw + w | |
translate(w,0,0) | |
pushMatrix() | |
rotate(ang,1,0,0) | |
self.m:draw() | |
popMatrix() | |
if not self.intouch and v ~= math.floor(v) then | |
tgt = math.floor(v + .5) | |
if math.abs(v - tgt) < .01 then | |
self.decimals[k] = tgt | |
else | |
self.decimals[k] = v + DeltaTime*(tgt- v)*10 | |
end | |
end | |
end | |
self.decwidth = tw | |
self.totalwidth = self.totalwidth + tw | |
end | |
--ortho() | |
resetMatrix() | |
stroke(Colour.svg.SlateBlue) | |
strokeWidth(6) | |
noFill() | |
noSmooth() | |
rectMode(CORNERS) | |
pushMatrix() | |
translate(x-self.totallwidth-5,y) | |
scale(self.totallwidth + self.decwidth+10,r) | |
self.overlay:draw() | |
popMatrix() | |
rect(x-self.totallwidth-5, | |
y-r-5, | |
x+self.decwidth+5, | |
y+r+5) | |
popMatrix() | |
popStyle() | |
end | |
end | |
function NumberSpinner:activate(t) | |
t = t or {} | |
self.pos = t.pos or self.pos | |
self.maxdigits = t.maxdigits or self.maxdigits | |
self.maxdecs = t.maxdecs or self.maxdecs | |
if t.allowSignChange ~= nil then | |
self.allowsign = t.allowSignChange | |
else | |
self.allowsign = true | |
end | |
local value = t.value or 0 | |
if value < 0 then | |
self.isneg = true | |
value = - value | |
end | |
local decs = value - math.floor(value) | |
value = math.floor(value) | |
local n = {} | |
local nn = 1 | |
n[1] = math.floor(value%10) | |
value = math.floor(value/10) | |
while value > 0 do | |
table.insert(n,math.floor(value%10)) | |
value = math.floor(value/10) | |
nn = nn + 1 | |
end | |
self.numbers = n | |
self.numdigits = nn | |
self.maxdigits = math.max(self.maxdigits,nn) | |
n = {} | |
nn = 0 | |
while decs > 0 and nn <= self.maxdecs do | |
decs = decs*10 | |
table.insert(n,math.floor(decs)) | |
decs = decs - math.floor(decs) | |
nn = nn + 1 | |
end | |
self.decimals = n | |
self.numdecs = nn | |
--self.maxdecs = math.max(nn,self.maxdecs) | |
self.active = true | |
self.action = t.action or function() return true end | |
end | |
function NumberSpinner:deactivate() | |
self.numbers = {0} | |
self.numdigits = 1 | |
self.decimals = {} | |
self.numdecs = 0 | |
self.maxdecs = 5 | |
self.maxdigits = 5 | |
self.isneg = false | |
self.allowsign = true | |
self.pos = self.ipos | |
self.active = false | |
self.action = function() return true end | |
end | |
function NumberSpinner:getValue() | |
local value = 0 | |
local ten = 1 | |
for k,v in ipairs(self.numbers) do | |
value = value + (math.floor(v+.5)%10)*ten | |
ten = ten * 10 | |
end | |
local ten = .1 | |
for k,v in ipairs(self.decimals) do | |
value = value + (math.floor(v+.5)%10)*ten | |
ten = ten * .1 | |
end | |
if self.isneg then | |
value = - value | |
end | |
return value | |
end | |
function NumberSpinner:isTouchedBy(touch) | |
if not self.active then | |
return false | |
end | |
local x,y = self.pos() | |
if touch.x < x - self.totalwidth then | |
self:deactivate() | |
return true | |
end | |
if touch.x > x + self.decwidth then | |
self:deactivate() | |
return true | |
end | |
if touch.y < y - self.radius then | |
self:deactivate() | |
return true | |
end | |
if touch.y > y + self.radius then | |
self:deactivate() | |
return true | |
end | |
self.intouch = true | |
return true | |
end | |
function NumberSpinner:processTouches(g) | |
if g.type.ended and g.type.tap and g.num == 2 then | |
local value = self:getValue() | |
if self.action and self.action(value) then | |
self:deactivate() | |
end | |
g:reset() | |
return | |
end | |
local x,y = self.pos() | |
local t = g.touchesArr[1] | |
if t.touch.state == BEGAN then | |
local n = math.floor((x - t.touch.x)/self.width + 1) | |
self.moving = n | |
else | |
if self.moving then | |
if self.moving > 0 then | |
if t.updated and self.numbers[self.moving] then | |
self.numbers[self.moving] = (self.numbers[self.moving] - t.touch.deltaY/HEIGHT*self.radius/10)%10 | |
end | |
if not self.isneg | |
and self.moving == self.numdigits | |
and self.allowsign | |
and g.type.tap | |
and g.type.finished | |
then | |
self.isneg = true | |
end | |
if self.numdigits < self.maxdigits | |
and t.updated | |
and t.touch.x < x - self.numwidth - self.width then | |
table.insert(self.numbers,0) | |
self.numdigits = self.numdigits + 1 | |
end | |
if t.updated | |
and self.moving == self.numdigits | |
and t.touch.x > x - self.numwidth + 2*self.width then | |
if self.numdigits > 1 then | |
table.remove(self.numbers) | |
self.numdigits = self.numdigits - 1 | |
else | |
self.numbers[1] = 0 | |
end | |
end | |
if self.moving == 1 and self.numdecs == 0 and self.maxdecs > 0 then | |
if t.updated | |
and t.touch.x > x + self.width then | |
table.insert(self.decimals,0) | |
self.numdecs = 1 | |
end | |
end | |
if self.isneg | |
and self.moving > #self.numbers | |
and self.allowsign | |
and g.type.tap | |
and g.type.finished then | |
self.isneg = false | |
end | |
else | |
if t.updated and self.decimals[-self.moving] then | |
self.decimals[-self.moving] = (self.decimals[-self.moving] - t.touch.deltaY/HEIGHT*self.radius/10)%10 | |
end | |
if self.numdecs < self.maxdecs | |
and t.touch.x > x + self.decwidth + 2*self.width then | |
table.insert(self.decimals,0) | |
self.numdecs = self.numdecs + 1 | |
end | |
if t.updated | |
and self.moving == -self.numdecs | |
and t.touch.x < x + self.decwidth - 2*self.width then | |
table.remove(self.decimals) | |
self.numdecs = self.numdecs - 1 | |
end | |
end | |
end | |
g:noted() | |
end | |
if (not g.type.tap and g.type.ended) or g.type.finished then | |
self.intouch = false | |
self.moving = nil | |
g:reset() | |
end | |
end | |
NumberSpinner.help = "Drag the wheels up and down to change the digits. Drag the outermost wheels to add or remove digits." | |
return NumberSpinner | |
--]==] |
This file contains hidden or 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
--[==[ | |
-- Palm Rest | |
local PalmRest = class() | |
cimport "RoundedRectangle" | |
cimport "Coordinates" | |
function PalmRest:init(t) | |
t = t or {} | |
self.orientation = t.orientation | |
self.height = t.height or 0 | |
local o = t.opacity or 127 | |
self.mesh = mesh() | |
local w = math.max(WIDTH,HEIGHT) | |
self.width = w | |
self.mesh:addRect(w/2,-w/2-5,w+10,w+10) | |
self.mesh:setRectColor(1,0,0,0,o) | |
addRoundedRect({ | |
mesh = self.mesh, | |
x = w/2, | |
y = 0, | |
width = 200, | |
height = 50, | |
corners = 9, | |
anchor = "south", | |
colour = color(0,0,0,o) | |
}) | |
addRoundedRect({ | |
mesh = self.mesh, | |
x = w/2, | |
y = 25, | |
width = 160, | |
height = 10, | |
radius = 3, | |
corners = 0, | |
anchor = "centre", | |
colour = color(127,127,127,o) | |
}) | |
self.active = t.active or true | |
end | |
function PalmRest:draw() | |
if not self.active then | |
return | |
end | |
pushMatrix() | |
resetMatrix() | |
if self.orientation then | |
TransformOrientation(self.orientation) | |
end | |
translate((WIDTH - self.width)/2,self.height) | |
self.mesh:draw() | |
popMatrix() | |
end | |
function PalmRest:isTouchedBy(touch) | |
if not self.active then | |
return false | |
end | |
local v | |
if self.orientation then | |
v = OrientationInverse(self.orientation,vec2(touch.x,touch.y)) | |
else | |
v = vec2(touch.x,touch.y) | |
end | |
if v.y < self.height then | |
return true | |
end | |
if v.y < self.height + 50 and math.abs(v.x - WIDTH/2) < 100 then | |
return true | |
end | |
return false | |
end | |
function PalmRest:processTouches(g) | |
for k,v in ipairs(g.touchesArr) do | |
if v.updated then | |
local x | |
if self.orientation then | |
x = OrientationInverse( | |
self.orientation, | |
vec2(v.touch.x,v.touch.y)) | |
else | |
x = vec2(v.touch.x,v.touch.y) | |
end | |
if v.touch.state == BEGAN then | |
if x.y < self.height then | |
v:destroy() | |
end | |
else | |
local y | |
if self.orientation then | |
y = OrientationInverse( | |
self.orientation, | |
vec2(v.touch.prevX,v.touch.prevY)) | |
else | |
y = vec2(v.touch.prevX,v.touch.prevY) | |
end | |
self.height = math.max(0,self.height + x.y - y.y) | |
end | |
end | |
end | |
if g.type.ended then | |
g:reset() | |
else | |
g:noted() | |
end | |
end | |
PalmRest.help = "The palm rest defines an area of the screen where touches are ignored. Drag the tab to set the height." | |
return PalmRest | |
--]==] |
This file contains hidden or 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
--[==[ | |
-- PictureBrowser | |
local PictureBrowser = class() | |
cimport "RoundedRectangle" | |
cimport "Coordinates" | |
local Colour = unpack(cimport "Colour",nil) | |
cimport "ColourNames" | |
function PictureBrowser:init() | |
end | |
function PictureBrowser:setList(t) | |
t = t or {} | |
self.width = t.width or 100 | |
self.height = t.height or 100 | |
self.anchor = t.anchor or "centre" | |
self.camera = t.camera | |
local d = t.directory or "Documents" | |
local f = t.filter or function(n,w,h) return true end | |
local l = spriteList(d) | |
local ims = {} | |
local img,w | |
local c = 0 | |
for k,v in ipairs(l) do | |
img = readImage(d .. ":" .. v) | |
if f(v,img.width,img.height) then | |
w = 100*math.min(1,img.width/img.height) | |
table.insert(ims,{img,w}) | |
c = c + 1 | |
end | |
end | |
if self.camera then | |
img = image(self.width,self.height) | |
setContext(img) | |
pushStyle() | |
pushMatrix() | |
resetStyle() | |
resetMatrix() | |
font("ArialRoundedMTBold") | |
fontSize(self.width/5) | |
fill(Colour.svg.LightGoldenrod) | |
RoundedRectangle(0,0,self.width,self.height,10) | |
fill(Colour.svg.Black) | |
text("Take",self.width/2,self.height/2 + self.width/10) | |
text("Photo",self.width/2,self.height/2 - self.width/10) | |
popStyle() | |
popMatrix() | |
setContext() | |
table.insert(ims,{img,100}) | |
c = c + 1 | |
end | |
self.images = ims | |
self.nimages = c | |
self.size = math.ceil(math.sqrt(c)) | |
self.active = t.active or false | |
self.sep = t.sep or 10 | |
self.fwidth = self.size*(self.width + self.sep) + self.sep | |
local n = math.ceil(self.nimages/self.size) | |
self.fheight = n*(self.width + self.sep) + self.sep | |
self.pos = t.pos or function() | |
return RectAnchorOf(Screen,"centre") | |
end | |
end | |
function PictureBrowser:draw() | |
if not self.active then | |
return | |
end | |
pushMatrix() | |
resetMatrix() | |
pushStyle() | |
resetStyle() | |
if self.usecamera then | |
spriteMode(CENTER) | |
local x,y = RectAnchorOf(Screen,"centre") | |
local w,h = spriteSize(CAMERA) | |
local ws = RectAnchorOf(Screen,"width") -10 | |
local hs = RectAnchorOf(Screen,"height") -10 | |
local asp = math.min(ws/w,hs/h,1) | |
sprite(CAMERA,x,y,w*asp,h*asp) | |
font("ArialRoundedMTBold") | |
fontSize(30) | |
fill(Colour.svg.LightGoldenrod) | |
ws = ws + 10 | |
RoundedRectangle(0,0,ws,70,10) | |
fill(Colour.svg.Black) | |
text("Tap to take picture",ws/2,50) | |
text("Swipe to change camera",ws/2,20) | |
else | |
spriteMode(CORNER) | |
local x,y = self.pos() | |
x,y = RectAnchorAt(x,y,self.fwidth,self.fheight,self.anchor) | |
local w = self.width + self.sep | |
RoundedRectangle(x,y,self.fwidth,self.fheight,self.sep) | |
y = y + self.fheight - w | |
x = x + self.sep | |
local xx = x | |
for i=1,self.nimages do | |
sprite(self.images[i][1],x,y,self.images[i][2]) | |
x = x + w | |
if x > xx + self.fwidth - w then | |
x = xx | |
y = y - w | |
end | |
end | |
end | |
popStyle() | |
popMatrix() | |
end | |
function PictureBrowser:isTouchedBy(touch) | |
if not self.active then | |
return false | |
end | |
if self.usecamera then | |
return true | |
end | |
local x,y = self.pos() | |
x,y = RectAnchorAt(x,y,self.fwidth,self.fheight,self.anchor) | |
if touch.x < x then | |
return false | |
end | |
if touch.y < y then | |
return false | |
end | |
if touch.x > x + self.fwidth then | |
return false | |
end | |
if touch.y > y + self.fheight then | |
return false | |
end | |
return true | |
end | |
function PictureBrowser:processTouches(g) | |
if g.type.ended then | |
if self.usecamera then | |
if g.type.moved then | |
if cameraSource() == CAMERA_FRONT then | |
cameraSource(CAMERA_BACK) | |
else | |
cameraSource(CAMERA_FRONT) | |
end | |
else | |
local img = image(CAMERA) | |
if self.callback(img) then | |
self:deactivate() | |
else | |
self.usecamera = false | |
end | |
end | |
else | |
local t = g.touchesArr[1] | |
local w = self.width + self.sep | |
local x,y = self.pos() | |
x,y = RectAnchorAt(x,y,self.fwidth,self.fheight,self.anchor) | |
y = y + self.fheight | |
x = math.ceil((t.touch.x - x)/w) | |
y = math.floor((y - t.touch.y)/w) | |
local n = self.size*y + x | |
if n > 0 and n <= self.nimages then | |
if self.camera and n == self.nimages then | |
self.usecamera = true | |
elseif self.callback(self.images[n][1]) then | |
self:deactivate() | |
end | |
end | |
end | |
g:reset() | |
else | |
g:noted() | |
end | |
end | |
function PictureBrowser:activate(f) | |
self.active = true | |
self.callback = f | |
self.usecamera = false | |
end | |
function PictureBrowser:deactivate() | |
self.active = false | |
self.callback = nil | |
self.usecamera = false | |
end | |
return PictureBrowser | |
--]==] |
This file contains hidden or 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
--[==[ | |
-- Slider class | |
-- Author: Andrew Stacey | |
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html | |
-- Licence: CC0 http://wiki.creativecommons.org/CC0 | |
--[[ | |
The "Slider" class draws a slider on the screen for the user to be | |
able to modify a parameter. When the slider is finished, a call-back | |
function is used to pass the new parameter value to the rest of the | |
program. | |
--]] | |
local Slider = class() | |
--[[ | |
Our initial information consists of our end points (as vec2 objects) | |
and a colour. | |
--]] | |
function Slider:init(t) | |
t = t or {} | |
self.oa = t.a | |
self.ob = t.b | |
self:orientationChanged() | |
local d = self.b - self.a | |
d = vec2(-d.y,d.x) | |
self.d = d/d:len() | |
self.t = 0 | |
self.fg = t.colour | |
self.bg = color(0,0,0,255) | |
end | |
function Slider:orientationChanged() | |
local x,y = self.oa() | |
self.a = vec2(x,y) | |
x,y = self.ob() | |
self.b = vec2(x,y) | |
end | |
--[[ | |
This draws the slider and the bar at the current parameter value. | |
--]] | |
function Slider:draw() | |
if self.active then | |
local a = self.a | |
local b = self.b | |
local t = self.t | |
pushStyle() | |
strokeWidth(10) | |
stroke(self.bg) | |
line(a.x,a.y,b.x,b.y) | |
strokeWidth(6) | |
stroke(self.fg) | |
line(a.x,a.y,b.x,b.y) | |
local c = t*a + (1 - t)*b | |
local d = 20 * self.d | |
a = c - d | |
b = c + d | |
strokeWidth(10) | |
stroke(self.bg) | |
line(a.x,a.y,b.x,b.y) | |
strokeWidth(6) | |
stroke(self.fg) | |
line(a.x,a.y,b.x,b.y) | |
end | |
end | |
--[[ | |
This is our activation function. Our arguments are: the current value | |
of the parameter, its minimum and maximum values, and two call-back | |
functions. The first is called when the slider is moved, the second | |
when it has finished moving. This means that the program can adjust | |
to changes in the value as they occur. | |
--]] | |
function Slider:activate(t,min,max,f,ff) | |
if type(t) == "table" then | |
min = t.min | |
max = t.max | |
f = t.action | |
ff = t.finalAction | |
t = t.value | |
end | |
self.active = true | |
t = (t - min)/(max - min) | |
self.t = math.min(math.max(0,t),1) | |
self.min = min | |
self.max = max | |
self.action = f | |
self.finalAction = ff | |
end | |
function Slider:deactivate() | |
self.active = false | |
self.action = nil | |
self.finalAction = nil | |
end | |
--[[ | |
If we are active, we try to claim all touches. | |
--]] | |
function Slider:isTouchedBy(touch) | |
if self.active then | |
return true | |
end | |
end | |
--[[ | |
We project the touch value onto the line and set the parameter value | |
accordingly, then call the appropriate call-back function. | |
We have to begin the touch near the slider, and there should be a way to cancel a slide ... | |
--]] | |
function Slider:processTouches(g) | |
if g.updated then | |
local t = g.touchesArr[1] | |
local a = self.a | |
local b = self.b | |
local c = a - b | |
local d = vec2(t.touch.x,t.touch.y) | |
d = d - b | |
local st = c:dot(d) | |
if t.touch.state == BEGAN then | |
local cl = c:len() | |
local ts = st/cl | |
if ts < -20 or ts > cl + 20 then | |
self:deactivate() | |
else | |
ts = ts/cl | |
local e = d - ts * c | |
if e:len() > 40 then | |
self:deactivate() | |
end | |
end | |
end | |
st = math.min(math.max(0,st/c:lenSqr()),1) | |
self.t = st | |
st = st*self.max + (1 - st)*self.min | |
if self.action then | |
self.action(st) | |
end | |
if g.type.ended then | |
self.active = false | |
if self.finalAction then | |
self.finalAction(st) | |
end | |
end | |
g:noted() | |
end | |
end | |
Slider.help = "The slider is used to change a continuous parameter between two given values. To change the parameter, drag the slider bar. To select a value, release the bar. Depending on how it was set up, you may see things change as you slide it, though it may be that not everything changes until the bar is released. You can cancel the slider at the start by touching some part of the screen well away from the slider." | |
return Slider | |
--]==] |
This file contains hidden or 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
--[==[ | |
-- User interface class | |
-- Author: Andrew Stacey | |
-- Website: http://www.math.ntnu.no/~stacey/HowDidIDoThat/iPad/Codea.html | |
-- Licence: CC0 http://wiki.creativecommons.org/CC0 | |
--[[ | |
This is a "User Interface" class. It is a container class that controls | |
the various pieces of the user interface: deciding whether they are drawn | |
and ensuring that they are registered with the "Touch Handler". | |
It is configured to use the "Touch Handler" class for touches, though | |
the interaction with that class is light so it could easily be | |
reconfigured to use some other system. It uses the "Font" class to | |
define a "System Font", though again this is more to ensure that the | |
elements in the user interface that need a font have one. | |
By default, the class initialises a keypad, a numeric pad, a "main | |
menu", two text areas (a small one for "System Messages" and a larger | |
one for "Information"), a colour picker, and a slider. Initially, | |
only the menu and the smaller text area are active (ie drawn): others | |
are activated on need. | |
The "User Interface" class provides some functions that are basically | |
wrappers around its elements. For example, there is a "getText(f)" | |
method which activates the keypad and allows the user to specify some | |
text string. The argument passed to this is a function which is | |
called when the user has finished specifying the string (such a | |
function is sometimes known as a "call-back"). This gets round the | |
problem that the program has to keep working while waiting for the | |
user to input the text. | |
Ideally, the "User Interface" is drawn last and is given first chance | |
at claiming a touch. This requires the user not to override these. | |
Also, temporary elements (such as the keypad) should be drawn on top | |
of the more permanent ones (such as the main menu) and the temporary | |
elements should be given priority for claiming touches. The class is | |
set up so that elements are drawn in the order in which they are | |
specified and touches are claimed in the reverse order. | |
--]] | |
local Font,_,Textarea = unpack(cimport "Font",nil) | |
cimport "Coordinates" | |
local Colour = unpack(cimport "Colour",nil) | |
local Menu,Keypad,ColourPicker,ColourWheel,Cubic,Keyboard, | |
Slider,NumberSpinner,PictureBrowser,FontPicker,PalmRest | |
local UI = class() | |
--[[ | |
This is the initialiser function. The sole argument is a "Touch | |
Handler" (though it would make sense to allow varying the font and the | |
initial family of elements). We declare ourselves "active" (meaning | |
that the user interface elements are drawn) and initialise a few | |
standard elements: keypad, numeric pad, menu, message area, | |
information area, colour picker, and slider (not all are initially | |
active. | |
--]] | |
function UI:init(t) | |
self.touchHandler = t | |
self.nelts = 0 | |
self.elements = {} | |
self.elementHandlers = t:unshiftHandlers() | |
self.systemfont = Font({name = "Courier", size = 16}) | |
self.largefont = Font({name = "Courier", size = 32}) | |
self.hugefont = Font({name = "Courier", size = 64}) | |
self.keyboards = {} | |
self.timers = {} | |
self.active = true | |
self.screen = Screen | |
self.orientation = CurrentOrientation | |
self:supportedOrientations(ANY) | |
if cmodule.loaded "Menu" then | |
Menu = cimport "Menu" | |
self.mainMenu = Menu({ | |
pos = function() | |
local x,y = RectAnchorOf(self.screen,"north west") | |
x = x + 10 | |
y = y - 10 | |
return x,y | |
end, | |
anchor = "north west", | |
font = self.systemfont}) | |
self.mainMenu.dir = "x" | |
self.mainMenu.active = true | |
self.mainMenu.autoactive = false | |
-- cancel stuff might not be needed any longer | |
self.cancel = Menu({ | |
pos = function() | |
local x,y = RectAnchorOf(self.screen,"north east") | |
x = x - 10 | |
y = y - 10 | |
return x,y | |
end, | |
anchor = "north east", | |
font = self.largefont | |
}) | |
self.cancel.active = false | |
self.cancel:addItem({ | |
title = "Cancel", | |
action = function() | |
self:cancelActivity() | |
end | |
}) | |
self.tocancel = {} | |
self:addElement( | |
self.mainMenu, | |
self.cancel | |
) | |
end | |
if cmodule.loaded "Keypad" then | |
Keypad = cimport "Keypad" | |
self.keypad = Keypad({ | |
pos = function() | |
local x,y = RectAnchorOf(self.screen,"west") | |
x = x + 50 | |
return x,y | |
end, | |
anchor = "west", | |
width = 75, | |
height = 50, | |
colour = Colour.svg.Magenta | |
}) | |
self.numpad = Keypad({ | |
pos = function() | |
local x,y = RectAnchorOf(self.screen,"west") | |
x = x + 50 | |
return x,y | |
end, | |
anchor = "west", | |
width = 75, | |
height = 50, | |
colour = Colour.svg.DeepPink, | |
numeric = true | |
}) | |
self:addElement( | |
self.keypad, | |
self.numpad | |
) | |
self:addHelp({ | |
title = "Key and numeric pads", | |
text = Keypad.help | |
}) | |
end | |
if cmodule.loaded "Colour" then | |
_,ColourPicker,ColourWheel = unpack(cimport "Colour") | |
self.colourPicker = ColourPicker() | |
self:addElement( | |
self.colourPicker | |
) | |
self:addHelp({ | |
title = "Colour Picker", | |
text = ColourPicker.help | |
}) | |
self.colourWheel = ColourWheel() | |
self:addElement( | |
self.colourWheel | |
) | |
self:addHelp({ | |
title = "Colour Wheel", | |
text = ColourWheel.help | |
}) | |
self.useWheel = true | |
end | |
if cmodule.loaded "CubicSelector" then | |
Cubic = cimport "CubicSelector" | |
self.cubic = Cubic(vec4(0,1,0,0)) | |
self:addElement(self.cubic) | |
self:addHelp({ | |
title = "Cubic Curve", | |
text = Cubic.help | |
}) | |
end | |
if cmodule.loaded "Slider" then | |
Slider = cimport "Slider" | |
self.slider = Slider({ | |
a = function() | |
local x,y = RectAnchorOf(self.screen,"west") | |
x = x + 50 | |
y = y + | |
math.min(200,2*RectAnchorOf(self.screen,"height")/3) | |
return x,y | |
end, | |
b = function() | |
local x,y = RectAnchorOf(self.screen,"west") | |
x = x + 50 | |
y = y - | |
math.min(200,2*RectAnchorOf(self.screen,"height")/3) | |
return x,y | |
end, | |
colour = Colour.svg.Coral | |
}) | |
self:addElement( | |
self.slider | |
) | |
self:addHelp({ | |
title = "Slider", | |
text = Slider.help | |
}) | |
end | |
if cmodule.loaded "NumberSpinner" then | |
NumberSpinner = cimport "NumberSpinner" | |
self.numberspinner = NumberSpinner({ | |
-- font = self.hugefont | |
pos = function() return RectAnchorOf(self.screen,"centre") end | |
}) | |
self:addElement( | |
self.numberspinner | |
) | |
self:addHelp({ | |
title = "Number Spinner", | |
text = NumberSpinner.help | |
}) | |
self.useSpinner = true | |
end | |
if cmodule.loaded "PictureBrowser" then | |
PictureBrowser = cimport "PictureBrowser" | |
self.picturebrowser = PictureBrowser() | |
self:addElement(self.picturebrowser) | |
end | |
if cmodule.loaded "FontPicker" then | |
FontPicker = cimport "FontPicker" | |
self.fontpicker = FontPicker() | |
self:addElement(self.fontpicker) | |
end | |
if cmodule.loaded "Font" then | |
_,_,Textarea = unpack(cimport "Font",nil) | |
self.messages = Textarea({ | |
font = self.systemfont, | |
pos = function() | |
return 10,10 | |
end, | |
width = "20em", | |
height = "4lh", | |
title = "System Messages" | |
}) | |
-- self.messages.active = true | |
self.information = Textarea({ | |
font = self.systemfont, | |
pos = function() | |
return RectAnchorOf(self.screen,"centre") | |
end, | |
width = "math.min(80em,WIDTH)", | |
height = "15lh", | |
anchor = "centre", | |
title = "Information", | |
vfill = true | |
}) | |
self.notices = Textarea({ | |
font = self.largefont, | |
pos = function() | |
local x,y = RectAnchorOf(self.screen,"centre") | |
return x,y | |
end, | |
width = "math.min(80em,WIDTH)", | |
height = "10lh", | |
anchor = "centre", | |
fit = true, | |
fadeTime = 2 | |
}) | |
self.helptxt = Textarea({ | |
font = self.systemfont, | |
pos = function() | |
local x,y = RectAnchorOf(self.screen,"north east") | |
y = y - 50 | |
return x,y | |
end, | |
width = "math.min(80em,WIDTH)", | |
height = "30lh", | |
anchor = "north east", | |
fit = true | |
}) | |
self:addElement( | |
self.notices, | |
self.helptxt, | |
self.information, | |
self.messages | |
) | |
self:addHelp({ | |
title = "Text boxes", | |
text = Textarea.help | |
}) | |
end | |
if cmodule.loaded "PalmRest" then | |
PalmRest = cimport "PalmRest" | |
self.palmrest = PalmRest({ | |
}) | |
self:addElement(self.palmrest) | |
self:addHelp({ | |
title = "Palm rest", | |
text = PalmRest.help | |
}) | |
end | |
if cmodule.loaded "Keyboard" then | |
Keyboard = cimport "Keyboard" | |
end | |
end | |
--[[ | |
This is the draw function. If we're active then we draw all our | |
elements (not all will actually draw: we just call their "draw" | |
method). We also ensure that if we're active then the main menu is | |
active (this is a sanity check as menus are sometimes deactivated by | |
their children). We ensure that the matrix and style are saved and | |
reset. | |
--]] | |
function UI:draw() | |
self:checkTimers() | |
if not self.active then | |
return false | |
end | |
pushMatrix() | |
pushStyle() | |
resetMatrix() | |
viewMatrix(matrix()) | |
ortho() | |
TransformOrientation(self.orientation) | |
for k=self.nelts,1,-1 do | |
self.elements[k]:draw() | |
end | |
popMatrix() | |
popStyle() | |
end | |
--[[ | |
This adds a new element, or new elements, to the user interface; for | |
example, a new menu. The arguments are a list of objects of | |
appropriate classes. | |
--]] | |
function UI:addElement(...) | |
for k,v in ipairs(arg) do | |
table.insert(self.elements,v) | |
table.insert(self.elementHandlers, | |
self.touchHandler:registerHandler(v)) | |
self.nelts = self.nelts + 1 | |
self:modifyTouchedBy(v) | |
self:modifyprocessTouches(v) | |
end | |
end | |
function UI:modifyTouchedBy(v) | |
local f = v.isTouchedBy | |
v.isTouchedBy = function(s,t) | |
t = TransformTouch(self.orientation,t) | |
return f(s,t) | |
end | |
end | |
function UI:modifyprocessTouches(v) | |
local f = v.processTouches | |
v.processTouches = function(s,g) | |
g:transformTouches(self.orientation) | |
return f(s,g) | |
end | |
end | |
function UI:activateElement(e) | |
local i,h | |
for k=self.nelts,1,-1 do | |
if self.elements[k] == e then | |
h = self.elementHandlers[k] | |
i = true | |
else | |
if i then | |
self.elements[k+1] = self.elements[k] | |
self.elementHandlers[k+1] = self.elementHandlers[k] | |
end | |
end | |
end | |
if i then | |
self.elements[1] = e | |
self.elementHandlers[1] = h | |
end | |
end | |
--[[ | |
This adds a submenu to the main menu. The argument is the title | |
(which appears in the main menu). It returns the newly created menu | |
which can then have elements added to it (see the "Menu" class for | |
--]] | |
function UI:addMenu(t) | |
t = t or {} | |
local x = t.x or 0 | |
local y = t.y or 0 | |
local pos = function() return x,y end | |
local ae = t.atEnd or false | |
local m | |
local mopts = {font = self.systemfont, pos = pos} | |
if not t.attach then | |
mopts.title = t.title | |
end | |
local tmopts = t.menuOpts or {} | |
for k,v in pairs(tmopts) do | |
mopts[k] = v | |
end | |
m = Menu(mopts) | |
if t.attach then | |
self.mainMenu:addItem({ | |
title = t.title, | |
action = function(x,y) | |
m.x = x | |
m.y = y | |
if m.active then | |
m:deactivateDown() | |
else | |
m:activate() | |
end | |
end, | |
deselect = function() | |
m:deactivateDown() | |
end, | |
highlight = function() | |
return m.active | |
end, | |
atEnd = ae | |
}) | |
end | |
self:addElement(m) | |
return m | |
end | |
--[[ | |
The following all initiate some activity. We need a cancellation method. | |
--]] | |
function UI:cancelActivity() | |
for k,v in ipairs(self.tocancel) do | |
v:deactivate() | |
end | |
self.tocancel = {} | |
self.cancel:deactivate() | |
end | |
--[[ | |
This is a wrapper around the keypad activation method. The argument | |
is a "call back" function that will be called when the keypad has | |
finished and the text entered by the user is available to the program | |
for use. | |
--]] | |
function UI:getText(f) | |
self:activateElement(self.keypad) | |
self.keypad:activate(f) | |
end | |
--[[ | |
This is similar to the "getText" function except that it uses the | |
numeric keypad instead of the normal keypad. | |
--]] | |
function UI:getNumberPad(f) | |
self:activateElement(self.numpad) | |
self.numpad:activate(f) | |
end | |
--[[ | |
This gets a number using a number spinner. | |
--]] | |
function UI:getNumberSpinner(...) | |
self:activateElement(self.numberspinner) | |
self.numberspinner:activate(...) | |
end | |
--[[ | |
This gets a number using the preferred method. | |
--]] | |
function UI:getNumber(f) | |
if self.useSpinner then | |
self:getNumberSpinner({action = f}) | |
else | |
self:getNumberPad(f) | |
end | |
end | |
--[[ | |
This activates the colour picker. The two arguments are the list of | |
colours to use and the call-back function. See the "ColourPicker" | |
class for details of what these arguments should be like. | |
--]] | |
function UI:getColourPicker(t,f) | |
self:activateElement(self.colourPicker) | |
self.colourPicker:setList(t) | |
self.colourPicker:activate(f) | |
end | |
--[[ | |
This activates the colour wheel. The argument is the call-back | |
function and initial/current colour. | |
--]] | |
function UI:getColourWheel(c,f) | |
self:activateElement(self.colourWheel) | |
self.colourWheel:activate(c,f) | |
end | |
--[[ | |
This gets a colour using the preferred method. | |
--]] | |
function UI:getColour(t,f) | |
if type(t) == "string" then | |
self:getColourPicker(t,f) | |
return | |
end | |
if type(t) == "function" then | |
t,f = f,t | |
end | |
if self.useWheel then | |
if not t then | |
t = Colour.svg.Black | |
end | |
self:getColourWheel(t,f) | |
else | |
if type(t) ~= "string" then | |
t = "" | |
end | |
self:getColourPicker(t,f) | |
end | |
end | |
--[[ | |
Gets a font name. | |
--]] | |
function UI:getFont(f) | |
self:activateElement(self.fontpicker) | |
self.fontpicker:activate({action = f}) | |
end | |
--[[ | |
Gets a picture as an image. | |
--]] | |
function UI:setPictureList(t) | |
self.picturebrowser:setList(t) | |
end | |
function UI:getPicture(f) | |
self:activateElement(self.picturebrowser) | |
self.picturebrowser:activate(f) | |
end | |
--[[ | |
This is a wrapper for the cubic curve object. | |
--]] | |
function UI:getCurve(...) | |
self:activateElement(self.cubic) | |
self.cubic:activate(...) | |
end | |
--[[ | |
This is a wrapper for the "slider" object, which allows the user to | |
specify a parameter by sliding a "slider". See the "Slider" class for | |
the allowed arguments to this function. | |
--]] | |
function UI:getParameter(...) | |
self:activateElement(self.slider) | |
self.slider:activate(...) | |
end | |
--[[ | |
This adds a message to the "System Messages" text area, also ensuring | |
that this is active. See the "Textarea" class for the parameters to | |
pass to this. | |
--]] | |
function UI:addMessage(...) | |
if not ui.messages then | |
return false | |
end | |
ui.messages:addLine(...) | |
ui.messages:activate() | |
end | |
--[[ | |
This is the same as "addMessage" except that it adds the message to | |
the "Information" text area. | |
--]] | |
function UI:addInformation(...) | |
if not ui.information then | |
return false | |
end | |
ui.information:addLine(...) | |
ui.information:activate() | |
end | |
--[[ | |
This is the same as "addMessage" except that it adds the message to | |
the "Notices" text area and sets a timer. | |
--]] | |
function UI:addNotice(t) | |
if not ui.notices then | |
return false | |
end | |
local time = t.time or 7 | |
if type(t.text) == "table" then | |
ui.notices:setLines(unpack(t.text)) | |
else | |
ui.notices:setLines(t.text) | |
end | |
ui.notices.fade = t.fadeTime or 2 | |
ui.notices:activate() | |
ui:setTimer(time,function() ui.notices:deactivate() return true end) | |
end | |
function UI:setTimer(t,f) | |
table.insert(self.timers,{t + ElapsedTime,f}) | |
end | |
function UI:checkTimers() | |
local rm,n = {},0 | |
for k,v in ipairs(self.timers) do | |
if ElapsedTime > v[1] then | |
if v[2]() then | |
table.insert(rm,k) | |
n = n + 1 | |
end | |
end | |
end | |
if n > 0 then | |
for k = n,1,-1 do | |
table.remove(self.timers,rm[k]) | |
end | |
end | |
end | |
function UI:reset() | |
for k,v in ipairs(self.elements) do | |
if v.reset and type(v.reset) == "function" then | |
v:reset() | |
end | |
end | |
end | |
function UI:pause(p) | |
for k,v in ipairs(self.elements) do | |
if v.pause and type(v.pause) == "function" then | |
v:pause(p) | |
end | |
end | |
end | |
function UI:orientationChanged(o) | |
if not self.allowedOrientations[o] then | |
return | |
end | |
self.orientation = o | |
self.screen = Screen | |
for k,v in ipairs(self.elements) do | |
if v.orientationChanged and type(v.orientationChanged) == "function" then | |
v:orientationChanged(o) | |
end | |
end | |
end | |
function UI:setOrientation(o,t) | |
if t then | |
self.allowedOrientations = t | |
end | |
o = ResolveOrientation(o,CurrentOrientation) | |
self.orientation = o | |
if o == PORTRAIT or o == PORTRAIT_UPSIDE_DOWN then | |
self.screen = Portrait | |
else | |
self.screen = Landscape | |
end | |
for k,v in ipairs(self.elements) do | |
if v.orientationChanged and type(v.orientationChanged) == "function" then | |
v:orientationChanged(o) | |
end | |
end | |
end | |
function UI:supportedOrientations(...) | |
self.allowedOrientations = {} | |
for _,v in ipairs(arg) do | |
if v < 4 then | |
self.allowedOrientations[v] = true | |
elseif v == 4 then | |
self.allowedOrientations[0] = true | |
self.allowedOrientations[1] = true | |
elseif v == 5 then | |
self.allowedOrientations[2] = true | |
self.allowedOrientations[3] = true | |
elseif v == 6 then | |
self.allowedOrientations[0] = true | |
self.allowedOrientations[1] = true | |
self.allowedOrientations[2] = true | |
self.allowedOrientations[3] = true | |
end | |
end | |
end | |
function UI:declareKeyboard(...) | |
local kbd = Keyboard(...) | |
ui:addElement(kbd) | |
self.keyboards[kbd.type] = kbd | |
return kbd | |
end | |
function UI:useKeyboard(t,f) | |
if self.keyboards[t] then | |
self:activateElement(self.keyboards[t]) | |
self.keyboards[t]:activate(f) | |
self.activekbd = self.keyboards[t] | |
end | |
end | |
function UI:unuseKeyboard(t) | |
if t then | |
if self.keyboards[t] then | |
self.keyboards[t]:deactivate() | |
if self.activekbd == self.keyboards[t] then | |
self.activekbd = nil | |
end | |
end | |
else | |
if self.activekbd then | |
self.activekbd:deactivate() | |
self.activekbd = nil | |
end | |
end | |
end | |
function UI:keyboardtop() | |
if self.activekbd then | |
return self.activekbd.top | |
else | |
return 0 | |
end | |
end | |
function UI:systemmenu() | |
local m = self:addMenu({title = "Main", attach = true}) | |
if fullscreen then | |
m:addItem({ | |
title = "Fullscreen", | |
action = function() | |
fullscreen() | |
return true | |
end, | |
highlight = function() | |
return displayMode() ~= STANDARD | |
end | |
}) | |
end | |
if pause then | |
m:addItem({ | |
title = "Pause", | |
action = function() | |
pause() | |
return true | |
end, | |
highlight = function() | |
return paused | |
end | |
}) | |
end | |
if reset then | |
m:addItem({ | |
title = "Reset", | |
action = function() | |
reset() | |
return true | |
end | |
}) | |
end | |
m:addItem({ | |
title = "Start Recording", | |
action = function() DoAtEndDraw(function() | |
if not isRecording() then | |
startRecording() | |
end | |
return true | |
end) | |
return true | |
end | |
}) | |
m:addItem({ | |
title = "Stop Recording", | |
action = function() DoAtEndDraw(function() | |
if isRecording() then | |
stopRecording() | |
end | |
return true | |
end) | |
return true | |
end | |
}) | |
if hide then | |
m:addItem({ | |
title = "Hide", | |
action = function() | |
hide() | |
return true | |
end | |
}) | |
end | |
m:addItem({ | |
title = "Reset touches", | |
action = function() | |
self.touchHandler:reset() | |
return true | |
end}) | |
m:addItem({ | |
title = "Exit", | |
action = function() | |
close() | |
return true | |
end}) | |
paused = false | |
return m | |
end | |
function UI:helpmenu() | |
local m = self:addMenu({title = "Help", attach = true, atEnd = true}) | |
self.helpm = m | |
if self.helps then | |
for k,v in ipairs(self.helps) do | |
m:addItem({ | |
title = v[1], | |
action = function() | |
if self.helptxt.active then | |
self.helptxt:deactivate() | |
else | |
self.helptxt:activate() | |
self.helptxt:setLines(unpack(v[2])) | |
end | |
return true | |
end | |
}) | |
end | |
end | |
return m | |
end | |
function UI:addHelp(t) | |
t = t or {} | |
local text = t.text | |
if type(text) ~= "table" then | |
text = {text} | |
end | |
table.insert(text,"(Double-tap this box to hide it.)") | |
if self.helpm then | |
self.helpm:addItem({ | |
title = t.title, | |
action = function() | |
if self.helptxt.active then | |
self.helptxt:deactivate() | |
else | |
self.helptxt:activate() | |
self.helptxt:setLines(unpack(text)) | |
end | |
return true | |
end | |
}) | |
else | |
self.helps = self.helps or {} | |
table.insert(self.helps,{t.title,text}) | |
end | |
end | |
function UI:removeHelp(t) | |
if self.helpm then | |
return self.helpm:removeItem(t) | |
elseif self.helps then | |
for k,v in ipairs(self.helps) do | |
if v.title == t then | |
table.remove(self.helps,k) | |
return v | |
end | |
end | |
end | |
end | |
function UI:hide(t) | |
self:addNotice({text = "The hidden things will return after " .. t .. " seconds"}) | |
self:setTimer(7,function() self.active = false return true end) | |
self:setTimer(t+7,function() unhide() return true end) | |
end | |
function UI:unhide() | |
self.active = true | |
end | |
--[[ | |
Helper functions to delay stuff to the end of the current draw function. To | |
make use of these, you must call AtEndOfDraw() in the draw function (at the | |
end, obviously). | |
--]] | |
function AtEndOfDraw() | |
local t = atenddraw or {} | |
local s = {} | |
for k,v in ipairs(t) do | |
if not v() then | |
table.insert(s,v) | |
end | |
end | |
atenddraw = s | |
end | |
function DoAtEndDraw(f) | |
atenddraw = atenddraw or {} | |
table.insert(atenddraw,f) | |
end | |
cmodule.gexport { | |
AtEndOfDraw = AtEndOfDraw, | |
DoAtEndDraw = DoAtEndDraw | |
} | |
return UI | |
--]==] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment