Created
September 26, 2015 05:55
-
-
Save anonymous/c01bc7d085daefdc5976 to your computer and use it in GitHub Desktop.
Gists Codea Upload
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# 1_Intro | |
-- 1_Intro_one_parameter | |
function setup() | |
title = "Sensor class demo" | |
code = "-------- \n(optimized for LANDSCAPE) \n---------" | |
info = | |
[[ | |
CODEA provides touch control via touched(touch) | |
but this is pretty basic, and we often miss more advanced | |
touch events. | |
Sensor() class provides a simple way to gather all gestures | |
you need so you can use them in your projects. | |
You can easily add your own gestures or modify existing ones. | |
In this version you'll find: | |
onTap, | |
onLongPress, | |
onSwipe, | |
onDrag, | |
onDrop, | |
onTouch, | |
onTouched, | |
onZoom(2 fingers gestire). | |
Swipe right/left to see examples, or use the slider. | |
Copy the example to paste it into a project. You can copy | |
just the demo main tab or the demo tab + the Sensor tab. | |
]] | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9/10 | |
xc,yc = WIDTH/2, HEIGHT*8/10 | |
xi,yi = WIDTH/2, HEIGHT*4/10 | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) textMode() | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(35) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(20) -- set font size | |
textAlign(CENTER) | |
text(code,xc,yc) | |
textAlign(LEFT) | |
text(info,xi,yi) | |
end | |
--# D2_onTap | |
-- 2_onTap | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
txt = "Tap me" | |
} | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onTap( function(event) | |
button.txt = button.txt .. "\n Outch!" | |
sound("A Hero's Quest:Hurt 1") | |
end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(255, 190, 0, 255) -- button color | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(50) -- set font size | |
textAlign(CENTER) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# D3_onLongPress | |
-- 3_onLongPress | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
txt = "Long press on me" | |
} | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onLongPress( function(event) | |
button.txt = button.txt .. "\n Outch!" | |
sound("A Hero's Quest:Hurt 1") | |
end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(255, 190, 0, 255) -- button color | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(50) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(button.w*1.8) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# D4_onSwipe | |
-- 4_onSwipe | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
txt = "Swipe on me left, right, up, down" | |
} | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onSwipe( function(event) | |
-- event.dx and event.dy give the amount of swipe in pixels | |
local dx,dy = 0,0 | |
if event.dx > 0 then dx = 1 end | |
if event.dx < 0 then dx = -1 end | |
if event.dy > 0 then dy = 1 end | |
if event.dy < 0 then dy = -1 end | |
local t1 = tween(0.5,button,{x=button.x+WIDTH/4*dx, y=button.y+HEIGHT/4*dy},tween.easing.cubicOut) | |
local t2 = tween(0.5,button,{x=button.x,y=button.y}, tween.easing.bounceOut) | |
tween.sequence(t1,t2) | |
sound("Game Sounds One:Male Cheer 2") | |
end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(255, 190, 0, 255) -- button color | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(50) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(button.w*1.8) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# D5_onDrag | |
-- 5_onDrag | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
txt = "drag me" | |
} | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onDrag( function(event) | |
-- event.touch containts current touch | |
local t = event.touch | |
button.x = button.x + t.deltaX | |
button.y = button.y + t.deltaY | |
end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(255, 190, 0, 255) -- button color | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(50) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(button.w*1.8) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# D6_onTouch | |
-- 6_onTouch | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
txt = "touch me (slide in, out)", | |
colorOff = color(255, 190, 0, 255), -- button color in false state | |
colorOn = color(0, 133, 255, 255), -- button color | |
state = false | |
} | |
button.update = function(self) | |
if self.state | |
then self.color = self.colorOn | |
else self.color = self.colorOff | |
end | |
end | |
button:update() | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onTouch( function(event) | |
button.state = event.state | |
button:update() | |
end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(button.color) | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(50) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(button.w*1.8) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# D7_onTouch_Fire | |
-- 7_onTouch_fire | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
txt = "touch me to fire", | |
colorOff = color(255, 190, 0, 255), -- button color in false state | |
colorOn = color(0, 133, 255, 255), -- button color | |
fire = false | |
} | |
button.update = function(self) | |
if self.fire then | |
self.color = self.colorOn | |
sound("Game Sounds One:Slap") | |
self.txt = self.txt .."\nBang!" | |
tween.delay(0.3, function() if button and button.update then button:update() end end) | |
else | |
self.color = self.colorOff | |
end | |
end | |
button:update() | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onTouch( function(event) | |
button.fire = event.state | |
button:update() | |
end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(button.color) | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(50) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(button.w*1.8) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# D8_onZoom | |
-- 8_onZoom | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
fontSize = 50, | |
txt = "pinch me or zoom me", | |
color = color(255, 190, 0, 255), -- button color in false state | |
} | |
button.deltaWH = function(self,dw,dh) | |
local surface = self.w * self.h | |
self.w = self.w + dw | |
self.h = self.h + dh | |
local newSurface = self.w * self.h | |
self.fontSize = self.fontSize * math.sqrt( newSurface / surface ) | |
end | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onZoom( function(event) | |
button:deltaWH( event.dw, event.dh) | |
end) | |
-- ######################################################### | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(button.color) | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(button.fontSize) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(button.w*1.8) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# D9_Drag_and_drop | |
-- 9_Drag and Drop | |
local Button = class() | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
button1 = Button("drag me", WIDTH/3*2+100,HEIGHT/3,50, color(0, 255, 0, 255) , "1") | |
button2 = Button("drag me", WIDTH/3*2+100,HEIGHT/3*2,50, color(0, 200, 0, 255) , "2") | |
button3 = Button("drag me,\ndrop on me", WIDTH/3,HEIGHT/2,150, color(255, 190, 0, 255)) | |
button3.on_Drop = function(self,obj) | |
local x,y | |
if obj.id == "1" then x = self.x - self.w + obj.w +10 end | |
if obj.id == "2" then x = self.x + self.w - obj.w -10 end | |
local y = self.y - self.h + obj.h +10 | |
tween(0.5, obj, {x=x,y=y}, tween.easing.bounceOut) | |
end | |
-- ############### and add events to sensors ################# | |
button1.sensor:onDrag( function(event) button1:on_Drag(event.touch) end ) | |
button2.sensor:onDrag( function(event) button2:on_Drag(event.touch) end ) | |
button3.sensor:onDrag( function(event) button3:on_Drag(event.touch) end ) | |
-- droppabled and drop-in objects should listen to onDrop, ... | |
button1.sensor:onDrop( function(event) end ) -- even if the callback does nothing | |
button2.sensor:onDrop( function(event) end ) | |
button3.sensor:onDrop( function(event) button3:on_Drop(event.object) end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
-- make sure your calls are in the good order | |
if button1:touched(t) then return true end | |
if button2:touched(t) then return true end | |
if button3:touched(t) then return true end | |
if intercepted then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
button3:draw() | |
button2:draw() | |
button1:draw() | |
end | |
function Button:init(txt,x,y,r,color,id) | |
self.x = x | |
self.y = y | |
self.w = r | |
self.h = r | |
self.txt = txt | |
self.color = color | |
self.id = id | |
self.sensor = Sensor{ parent=self, xywhMode=RADIUS } | |
end | |
function Button:on_Drag( t ) | |
self.x = self.x + t.deltaX | |
self.y = self.y + t.deltaY | |
end | |
function Button:touched(t) | |
return self.sensor:touched(t) | |
end | |
function Button:draw() | |
noStroke() | |
fill(self.color) -- button color | |
rectMode(RADIUS) | |
rect(self.x,self.y,self.w,self.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(20) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(self.w*1.8) | |
text(self.txt,self.x,self.y) -- draw text on screen) | |
end | |
--# D10_multiple_events | |
-- 10_multiple_events | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
local txt = "touch, tap, swipe" | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
txt = txt, | |
colorOff = color(255, 190, 0, 255), -- button color in false state | |
colorOn = color(0, 133, 255, 255), -- button color | |
state = false | |
} | |
button.update = function(self) | |
if self.state | |
then self.color = self.colorOn | |
else self.color = self.colorOff | |
end | |
end | |
button:update() | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onTouch( function(event) | |
button.state = event.state | |
button:update() | |
end) | |
button.sensor:onTap( function(event) | |
button.txt = txt .."\n".."tapped" | |
end) | |
button.sensor:onSwipe( function(event) | |
local str = "swipe:\n dx = "..tostring(event.dx).."\n dy = "..tostring(event.dy) | |
button.txt = txt .."\n"..str | |
end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(button.color) | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(50) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(button.w*1.8) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# D11_onTouched | |
-- 6_onTouched | |
function setup() | |
-- prepare any object with x,y,w,h fields, here a button | |
local txt = "Sometime you just want to get std Codea touch events" | |
button = { | |
x = WIDTH/2, | |
y = HEIGHT/2, | |
w = WIDTH/4, | |
h = HEIGHT/4, | |
txt = txt, | |
color = color(255, 190, 0, 255), -- button color in false state | |
} | |
-- ############### and add a Sensor to it ################# | |
button.sensor = Sensor{ parent=button, xywhMode=RADIUS } | |
button.sensor:onTouched( function(event) | |
local t = event.touch | |
local str = "x= "..tostring(t.x).." y= "..tostring(t.y) | |
button.txt = txt .. "\n" .. str | |
if t.state==ENDED then button.txt = txt end | |
end) | |
-- ############### ################# ################# | |
end | |
function touched(t) | |
-- ######### add this in your touched function ######### | |
if button.sensor:touched(t) then return true end | |
end | |
function draw() | |
noStroke() | |
background(178, 178, 178, 255) | |
fill(button.color) | |
rectMode(RADIUS) | |
rect(button.x,button.y,button.w,button.h) | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(25) -- set font size | |
textAlign(CENTER) | |
textWrapWidth(button.w*1.8) | |
text(button.txt,button.x,button.y) -- draw text on screen) | |
end | |
--# Credits | |
function setup() | |
title = "THE END" | |
code = "--------" | |
info = | |
[[ | |
Well that's all for now. | |
-------- | |
CREDITS: | |
this Sensor and tuto was cooked up by: JMV38 | |
]] | |
textColor = color(255) | |
xt,yt = WIDTH/2, HEIGHT*9/10 | |
xc,yc = WIDTH/2, HEIGHT*8/10 | |
xi,yi = WIDTH/2, HEIGHT*4.5/10 | |
end | |
function draw() | |
background(178, 178, 178, 255) textMode() | |
textWrapWidth(WIDTH*0.9)-- controls the wraping of long texts | |
fill(57, 57, 57, 255) -- set color for text | |
fontSize(35) -- set font size | |
text(title,xt,yt) -- draw text on screen | |
fontSize(25) -- set font size | |
text(code,xc,yc) | |
textAlign(CENTER) | |
text(info,xi,yi) | |
textAlign( LEFT) | |
end | |
--# Main | |
-- Main | |
-- This part is not part of the tutorial, it is managing the transitions | |
-- => the code is not commented | |
function setup() | |
supportedOrientations(LANDSCAPE_LEFT) | |
-- the demo tabs | |
demo = listProjectTabs() | |
-- remove the last tabs starting at Main tab | |
local thisIsNoDemoTab = false | |
for i = 1,#demo do | |
if demo[i] == "Main" then thisIsNoDemoTab = true end | |
if thisIsNoDemoTab then demo[#demo] = nil end | |
end | |
print("Swipe left/right to go to next demo tab") | |
print("Or use the top right selector to jump quickly to a demo tab") | |
-- I use 2 images to make the transition between demos smoother, and for the workbench | |
img1 = image(WIDTH,HEIGHT) | |
img2 = image(WIDTH,HEIGHT) | |
position = {dx1=0,dx2=WIDTH} -- a table to keep the image position | |
message = "" -- some text to display | |
touchable = true -- flag to disable touch during transitions | |
-- a quick browsing slider | |
slider = Slider({list=demo, x=WIDTH-160, y=HEIGHT-200, w=300, h=40, fontSize=20, | |
callback = function(i,tab) setTab(i) end}) | |
-- let's start by running the first tab of the list | |
setTab(1) | |
-- a screen swipe sensor | |
botScreen = {x=0,y=0,w=WIDTH,h=HEIGHT} | |
botScreen.sensor = Sensor {parent=botScreen} | |
botScreen.sensor:onSwipe(function(event) | |
if event.dx < 0 and current<#demo then setTab(current + 1) end | |
if event.dx > 0 and current>1 then setTab(current - 1) end | |
end) | |
-- a screen to sense any touch | |
topScreen = {x=0,y=0,w=WIDTH,h=HEIGHT} | |
topScreen.sensor = Sensor {parent=topScreen} | |
topScreen.sensor.doNotInterceptTouches = true | |
touches = {} -- collect touches for the video only | |
topScreen.sensor:onTouched(function(event) | |
local t = event.touch | |
if t.state == BEGAN or t.state == MOVING then | |
touches[t.id] = {x=t.x, y=t.y} | |
else | |
touches[t.id] = nil | |
end | |
end | |
) | |
end | |
function setTab(i) | |
if not current then current = i currentTab = demo[i] end | |
runTab(i)() | |
current = i | |
currentTab = demo[i] | |
slider:setCur(i) | |
copyButtonMenu() | |
end | |
function copyButtonMenu() | |
-- clear | |
parameter.clear() | |
message = "" | |
-- if this is not a demo tab, no copy button | |
if currentTab:sub(1,1) == "D" then | |
-- a <copy> button so you can quickly get the tab code | |
parameter.action("copy Main and Sensor",function() | |
pasteboard.text = "--".."# Main\n"..readProjectTab(currentTab) .. | |
"\n".."--".."# Sensor\n"..readProjectTab("Sensor") | |
print(currentTab .. " and Sensor have been copied. You can now paste it into a new project.") | |
end ) | |
parameter.action("copy Main code only",function() | |
pasteboard.text = readProjectTab(currentTab) | |
print(currentTab .. " copied. You can now paste it into a project.") | |
end ) | |
-- show tab name | |
message = currentTab | |
end | |
if currentTab == "Workbench" then | |
-- a <copy> button so you can quickly get the blendmode | |
parameter.action("copy blendmode",function() | |
pasteboard.text = blend:tostring() | |
print(pasteboard.text) | |
end ) | |
end | |
end | |
function mainDraw() | |
background(0) | |
drawDemo() | |
drawMessage() | |
slider:draw() | |
noStroke() | |
fill(255,128) | |
for k,t in pairs(touches) do | |
ellipse(t.x,t.y,35) | |
end | |
end | |
function runTab(i) | |
local callback = function() | |
-- get tab content | |
local name = demo[i] | |
local tab = readProjectTab(name) | |
-- execute it (calling setup is necessary to setup new values) | |
draw = function() end | |
touched = function() end | |
loadstring(tab)() | |
drawDemo = draw | |
touchedDemo = touched | |
setup() | |
-- draw once into an image, and then re-activate the main draw function | |
img2 = draw_into_image() | |
-- this is to make a nice transition between demo tabs | |
animateTransition(i) | |
end | |
return callback | |
end | |
function animateTransition(new) | |
local direction = compare(current,new) | |
position.dx1= 0 | |
position.dx2= WIDTH * direction | |
touchable = false | |
draw = transitionDraw | |
tween(1, position, {dx1= -WIDTH * direction ,dx2=0}, tween.easing.cubicInOut) | |
tween.delay(1,function() | |
position.dx1=0 | |
position.dx2=WIDTH | |
img1 = img2 | |
collectgarbage() | |
touchable = true | |
draw = mainDraw | |
touched = mainTouched | |
end) | |
currentTab = name | |
end | |
function transitionDraw() | |
-- during transition, 2 fixed images are moved on the screen (no live update) | |
background(0) | |
spriteMode(CORNER) | |
sprite(img1, position.dx1, 0) -- this image is the main one | |
sprite(img2, position.dx2, 0) -- this one is used for transition effects | |
end | |
function compare(v1,v2) | |
if v1 < v2 then return 1.0 end | |
if v1 == v2 then return 0.0 end | |
if v1 > v2 then return -1.0 end | |
end | |
function mainTouched(t) | |
topScreen.sensor:touched(t) | |
if slider:touched(t) then return true end | |
if touchedDemo(t) then return true end | |
if botScreen.sensor:touched(t) then return true end | |
end | |
function drawMessage() | |
if message == "" then return end | |
resetStyle() | |
fill(128) | |
rectMode(CENTER) | |
local x,y = WIDTH/2,HEIGHT-50 | |
rect(x,y,300,50) | |
fill(255) | |
fontSize(25) | |
text(message,x,y) | |
end | |
function draw_into_image() | |
local img = image(WIDTH,HEIGHT) | |
setContext(img) | |
draw() | |
setContext() | |
return img | |
end | |
--# Sensor | |
-- Sensor | |
-- a class to interpret touch events | |
-- usage: | |
--[[ | |
-- in setup(): | |
screen = {x=0,y=0,w=WIDTH,h=HEIGHT} | |
sensor = Sensor {parent=screen} -- tell the object you want to be listening to touches, here the screen | |
sensor:onTap( function(event) print("tap") end ) | |
-- in touched(t): | |
if sensor:touched(t) then return true end | |
-- available: | |
sensor:onDrag(callback) | |
sensor:onTap(callback) | |
sensor:onLongPress(callback) | |
sensor:onSwipe(callback) | |
--]] | |
Sensor = class() | |
function Sensor:init(t) | |
self.enabled = true -- listen to touches | |
self.extra = t.extra or self.extra or 0 -- enlarge sensitive zone for small dots or fat fingers | |
self.touches = {} | |
self:setParent(t) | |
self.events = {} | |
self.doNotInterceptTouches = false | |
end | |
function Sensor:setParent(t) | |
-- a parent must have x,y,w,h coordinates (CORNER) to use the sensor | |
local p = t.parent or self.parent | |
if p.x and p.y and p.w and p.h then | |
self.parent = p | |
else | |
error("Sensor parent must have x,y,w,h coordinates") | |
end | |
-- the coordinates may be in different modes, use the appropriate function | |
self.xywhMode = t.xywhMode or self.xywhMode or CORNER | |
if self.xywhMode == CENTER then self.xywh = self.xywhCENTER | |
elseif self.xywhMode == CORNER then self.xywh = self.xywhCORNER | |
elseif self.xywhMode == RADIUS then self.xywh = self.xywhRADIUS | |
end | |
end | |
local abs = math.abs | |
-- all gestures register themselves with this function | |
function Sensor:register(eventName, update, callback) | |
table.insert(self.events, {name=eventName, callback=callback, update=update}) | |
end | |
-- gestures defined below. Note the that, because gestures are managed individually, the | |
-- code is much more clear than when everything is mixed up. And only the needed computations are done. | |
-- zoom gesture | |
function Sensor:onZoom(callback) | |
self:register("onZoom", self.zoomUpdate, callback) | |
end | |
function Sensor.zoomUpdate(event,self,t) | |
event.touches = event.touches or {} -- init table | |
local touches = event.touches | |
local t1 = touches[1] | |
local t2 = touches[2] | |
if t.state == BEGAN then -- a new finger has come | |
if #touches >= 2 then | |
-- this is a 3rd finger, dont use it | |
else | |
-- register this touch and reset | |
table.insert(touches,t) | |
end | |
elseif t.state == MOVING then | |
-- this is a zoom, if we have exactly 2 touches and t is one of them | |
if t1 and t2 and ( t1.id == t.id or t2.id == t.id ) then | |
local tm,ts -- m moving, s static | |
if t1.id == t.id | |
then touches[1]=t ; tm = t ; ts = t2 | |
else touches[2]=t ; tm = t ; ts = t1 | |
end | |
local dw,dh | |
if tm.x>ts.x | |
then dw = tm.deltaX | |
else dw = - tm.deltaX | |
end | |
if tm.y>ts.y | |
then dh = tm.deltaY | |
else dh = - tm.deltaY | |
end | |
event.dw = dw | |
event.dh = dh | |
event:callback() | |
end | |
else | |
if t1 and t1.id == t.id then table.remove(touches,1) end | |
if t2 and t2.id == t.id then table.remove(touches,2) end | |
end | |
end | |
-- drag gesture | |
function Sensor:onDrag(callback) | |
self:register("onDrag", self.dragUpdate, callback) | |
end | |
function Sensor.dragUpdate(event,self,t) | |
if self.touches[t.id] then | |
event.touch = t | |
event:callback() | |
end | |
end | |
-- drop gesture | |
function Sensor:onDrop(callback) | |
self:register("onDrop", self.dropUpdate, callback) | |
end | |
local droppedObject, droppedTime | |
function Sensor.dropUpdate(event,self,t) | |
if self:inbox(t) and t.state == ENDED then | |
if droppedTime ~= ElapsedTime then | |
droppedTime = ElapsedTime | |
droppedObject = self.parent | |
self.doNotInterceptOnce = true | |
else | |
event.object = droppedObject | |
event:callback() | |
end | |
end | |
end | |
-- touched gesture (this is like COUDEA touched function) | |
function Sensor:onTouched(callback) | |
self:register("onTouched", self.touchedUpdate, callback) | |
end | |
function Sensor.touchedUpdate(event,self,t) | |
if self:inbox(t) then | |
event.touch = t | |
event:callback() | |
end | |
end | |
-- touch gesture | |
function Sensor:onTouch(callback) | |
self:register("onTouch", self.touchUpdate, callback) | |
end | |
function Sensor.touchUpdate(event,self,t) | |
self.touching = self.touching or {} -- track touches, not only BEGAN | |
-- current touch | |
if self:inbox(t) then | |
if t.state == BEGAN or t.state == MOVING then | |
self.touching[t.id] = true -- this is touching | |
else | |
self.touching[t.id] = nil -- this is not | |
end | |
else | |
self.touching[t.id] = nil | |
end | |
-- final state | |
local state1 = false -- one touch is enough to be touched | |
for i,t in pairs(self.touching) do state1= true ; break end | |
--if state has changed, send callback | |
if state1 ~= event.state then | |
event.state = state1 | |
event.touch = t | |
event:callback() | |
end | |
end | |
-- tap gesture | |
function Sensor:onTap(callback) | |
self:register("onTap", self.tapUpdate, callback) | |
end | |
function Sensor.tapUpdate(event,self,t) | |
if self.touches[t.id] then -- the touch must have started on me | |
if t.state == BEGAN then | |
event.totalMove = 0 | |
event.t0 = ElapsedTime | |
elseif t.state == MOVING then | |
-- integrate finger movement | |
event.totalMove = event.totalMove + abs(t.deltaX) + abs(t.deltaY) | |
elseif t.state == ENDED | |
and event.totalMove < 10 -- the finger should not have moved too much ... | |
and (ElapsedTime-event.t0) < 0.5 then -- and delay should not be too long | |
event:callback() | |
end | |
end | |
end | |
-- long press gesture | |
function Sensor:onLongPress(callback) | |
self:register("onLongPress", self.longPressUpdate, callback) | |
end | |
function Sensor.longPressUpdate(event,self,t) | |
local tmin = 1 | |
if self.touches[t.id] then -- the touch must have started on me | |
if t.state == BEGAN then | |
event.totalMove = 0 | |
event.cancel = false | |
event.id = t.id | |
event.tween = tween.delay(tmin,function() | |
event.tween = nil | |
if event.totalMove > 10 or event.id ~= t.id then event.cancel = true end | |
if event.cancel then return end | |
event:callback() | |
end) | |
elseif t.state == MOVING and event.id == t.id then | |
-- integrate finger movement | |
event.totalMove = event.totalMove + abs(t.deltaX) + abs(t.deltaY) | |
elseif (t.state == ENDED or t.state == CANCELLED) and event.id == t.id then | |
event.cancel = true | |
if event.tween then tween.stop(event.tween) end | |
end | |
end | |
end | |
-- swipe gesture | |
function Sensor:onSwipe(callback) | |
self:register("onSwipe", self.swipeUpdate, callback) | |
end | |
function Sensor.swipeUpdate(event,self,t) | |
if self.touches[t.id] then -- the touch must have started on me | |
if t.state == BEGAN then | |
event.dx = 0 | |
event.dy = 0 | |
event.t0 = ElapsedTime | |
elseif t.state == MOVING then | |
-- track net finger movement | |
event.dx = event.dx + t.deltaX | |
event.dy = event.dy + t.deltaY | |
elseif t.state == ENDED | |
and (ElapsedTime-event.t0) < 1 then -- delay should not be too long | |
-- and the finger should have moved enough: | |
local minMove = 70 | |
if abs(event.dx) < minMove then event.dx = 0 end | |
if abs(event.dy) < minMove then event.dy = 0 end | |
if event.dx ~= 0 or event.dy ~= 0 then | |
event:callback() -- use event.dx and .dy to know the swipe direction | |
end | |
end | |
end | |
end | |
function Sensor:touched(t) | |
if not self.enabled then return end | |
if t.state == BEGAN and self:inbox(t) then | |
self.touches[t.id] = true | |
end | |
for i,event in ipairs(self.events) do | |
event:update(self,t) -- only registered events are computed | |
end | |
local intercepted = self.touches[t.id] | |
if self.doNotInterceptOnce then | |
intercepted = false | |
self.doNotInterceptOnce = false | |
end | |
if t.state == ENDED or t.state == CANCELLED then | |
self.touches[t.id] = nil | |
end | |
-- return true when touched (or concerned) | |
if self.doNotInterceptTouches then intercepted = false end | |
return intercepted | |
end | |
-- functions to get x, y, w, h in different coordinates systems | |
function Sensor:xywhCORNER() | |
local p = self.parent | |
local wr, hr = p.w/2.0, p.h/2.0 | |
local xr, yr = p.x + wr, p.y + hr | |
return xr,yr,wr,hr | |
end | |
function Sensor:xywhCENTER() | |
local p = self.parent | |
return p.x, p.y, p.w/2, p.h/2 | |
end | |
function Sensor:xywhRADIUS() | |
local p = self.parent | |
return p.x, p.y, p.w, p.h | |
end | |
-- check if the box is touched | |
function Sensor:inbox(t) | |
local x,y,w,h = self:xywh() | |
return abs(t.x-x)<(w+self.extra) and abs(t.y-y)<(h+self.extra) | |
end | |
--# Selector | |
-- A class to make a selection from a list of values | |
Selector = class() | |
function Selector:init(data) | |
data = data or {} | |
self.list = data.list or self.list or {"a","b","c"} | |
self.cur = data.cur or self.cur or 1 -- currently selected index | |
self.title = data.title or self.title | |
self.tdx = data.tdx or self.tdx or -30 | |
self.tdy = data.tdy or self.tdy or 0 | |
self.h = data.h or self.h or 50 | |
self.w = data.w or self.w or 200 | |
self.x = data.x or self.x or 300 | |
self.y = data.y or self.y or 300 | |
self.fontSize = data.fontSize or self.fontSize or self.h * 0.8 | |
self.titleFontSize = data.titleFontSize or self.titleFontSize or self.fontSize | |
self.bkg = data.bkg or self.bkg or color(230) | |
self.frg = data.frg or self.frg or color(0) | |
self.stroke = data.stroke or self.stroke or color(100) | |
self.strokeWidth = data.strokeWidth or self.strokeWidth or 5 | |
-- callback is called with 2 arguments: callback( current index , current list value ) | |
self.callback = data.callback or self.callback or function(v) print(v) end | |
-- prepare the image for drawing | |
self.img = self:makeImage() | |
self.dy = self:update_dy() | |
self.visible = true | |
-- for touch management | |
self.wr = self.w / 2 | |
self.hr = self.h / 2 | |
self.xr = self.x + self.wr | |
self.yr = self.y + self.hr | |
self.sensor = Sensor{parent=self} | |
self.sensor:onDrag( function(event) self:drag(event.touch) end ) | |
self.sensor.enabled = (#self.list~=1) -- if 1 option only, this is just a text box | |
end | |
function Selector:update_dy() | |
-- update the image shift from current index value | |
self.dy = (self.cur-1) * self.h | |
return self.dy | |
end | |
function Selector:update_cur() | |
-- update current index from dy value | |
self.cur = math.floor(self.dy/self.h+1.5) | |
if self.cur < 1 then self.cur =1 end | |
if self.cur > #self.list then self.cur =#self.list end | |
end | |
function Selector:setCur(v) | |
self.cur = v | |
if self.cur < 1 then self.cur =1 end | |
if self.cur > #self.list then self.cur =#self.list end | |
self:update_dy() | |
end | |
function Selector:setValue(target) | |
for i,v in ipairs(self.list) do | |
if v == target then | |
self:setCur(i) | |
end | |
end | |
end | |
function Selector:makeImage() | |
-- an image with all values | |
self.htot = self.h * (#self.list) | |
self.dyMax = self.htot - self.h | |
local img = image(self.w, self.htot) | |
local x,y = self.w/2 | |
setContext(img) | |
do | |
background( self.bkg ) | |
fill(self.frg) | |
fontSize(self.fontSize) | |
textMode(CENTER) | |
for i,v in ipairs(self.list) do | |
y = self.h * (i-0.5) | |
text( tostring(v) , x,y) | |
end | |
end | |
setContext() | |
return img | |
end | |
function Selector:draw() | |
if not self.visible then return end | |
-- draw the current value | |
spriteMode(CORNER) | |
clip(self.x,self.y,self.w,self.h) | |
sprite(self.img, self.x, self.y-self.dy) | |
clip() | |
-- add some decoration | |
stroke(self.stroke) | |
strokeWidth(self.strokeWidth) | |
fill(0,0) | |
rectMode(CORNER) | |
rect(self.x-1,self.y-1,self.w+2,self.h+2) | |
-- and the title | |
if self.title then | |
fontSize(self.titleFontSize) | |
fill(self.frg) | |
textMode(CORNER) | |
text(self.title,self.x+self.tdx, self.y+self.tdy) | |
end | |
end | |
function Selector:touched(t) | |
self.sensor:touched(t) | |
end | |
function Selector:drag(t) | |
if t.state == MOVING then | |
-- drag the image | |
self.dy = self.dy - t.deltaY | |
if self.dy < 0 then self.dy =0 end | |
if self.dy > self.dyMax then self.dy =self.dyMax end | |
elseif t.state == ENDED then | |
-- when finger released, finalize the value | |
self:update_cur() | |
self:update_dy() | |
self.callback( self.cur , self.list[ self.cur ] ) | |
end | |
end | |
--# Dot | |
Dot = class() | |
-- just a dot object | |
function Dot:init(t) | |
self.x = t.x or WIDTH/2 | |
self.y = t.y or HEIGHT/2 | |
self.r = t.r or 50 | |
self.w = self.r | |
self.h = self.r | |
end | |
function Dot:draw() | |
stroke(0) | |
strokeWidth(2) | |
fill(200) | |
ellipseMode(RADIUS) | |
ellipse(self.x,self.y,self.r) | |
end | |
function Dot:touched(touch) | |
end | |
--# Slider | |
Slider = class() | |
function Slider:init(data) | |
-- slider geometry | |
self.x0 = WIDTH - 20 | |
self.x1 = self.x0 | |
self.y0 = HEIGHT*0.7 | |
self.y1 = HEIGHT*0.1 | |
self.dy = 0 | |
self.dyMax = self.y0-self.y1 | |
-- manage and view the demo tabs | |
self.selector = Selector(data) | |
self.ratio = (self.y0-self.y1)/(self.selector.h * (#self.selector.list-1)) | |
self.selector.x = self.x0 - self.selector.w - 50 | |
self.selector.y = self.y0 - self.selector.h/2 | |
self.selector.visible = false | |
-- a draggable dot to move the slider | |
self.dot = Dot{ x=self.x0, y=self.y0+self.dy, r=16} | |
self.dotSensor = Sensor{ parent=self.dot, xywhMode=RADIUS, extra=10} | |
self.dotSensor:onDrag( function(event) self:drag(event.touch) end ) | |
end | |
-- set a choice in the selector list | |
function Slider:setCur(v) | |
self.selector:setCur(v) | |
self.dy = (v-1) * self.selector.h * self.ratio | |
self.selector.y = self.y0 - self.selector.h/2 - self.dy | |
self.dot.y = self.y0 - self.dy | |
end | |
function Slider:draw() | |
-- slider line | |
smooth() | |
stroke(0) | |
strokeWidth(10) | |
line(self.x0,self.y0,self.x1,self.y1) | |
stroke(200) | |
strokeWidth(6) | |
line(self.x0,self.y0,self.x1,self.y1) | |
-- slider dot | |
self.dot:draw() | |
self.selector:draw() | |
end | |
function Slider:drag(t) | |
local s = self.selector | |
if t.state == BEGAN then | |
s.visible = true | |
elseif t.state == MOVING then | |
self.dy = self.dy - t.deltaY | |
if self.dy < 0 then self.dy =0 end | |
if self.dy > self.dyMax then self.dy =self.dyMax end | |
s.dy = self.dy/self.ratio | |
s.y = self.y0 - s.h/2 - self.dy | |
self.dot.y = self.y0 - self.dy | |
elseif t.state == ENDED then | |
-- when finger released, finalize the value | |
s:update_cur() | |
s:update_dy() | |
s.callback( s.cur , s.list[ s.cur ] ) | |
s.visible = false | |
end | |
end | |
function Slider:touched(t) | |
self.dotSensor:touched(t) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment