Skip to content

Instantly share code, notes, and snippets.

Created September 26, 2015 05:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/c01bc7d085daefdc5976 to your computer and use it in GitHub Desktop.
Save anonymous/c01bc7d085daefdc5976 to your computer and use it in GitHub Desktop.
Gists Codea Upload
--# 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