Some of you might get a kick out of this. After watching Bret Victor’s talk I spent a few hours writing an “instant” version of Codea inside Codea. Basically it gives you a keyboard and you can write Codea code, any code you write is instantly run and drawn behind your code.
It’s pretty interesting. Here’s the code and a screenshot.
Main file is Here: http://pastebin.com/8enSYdgZ — does not paste into forums due to Emoji.
Buffer
Buffer = class()
function Buffer:init()
self.buffer = {}
self.font = "Inconsolata"
self.fontSize = 20
-- x = line, y = pos
self.cursor = vec2(1,1)
self.t = 0
self.cursorBlink = 0
end
function Buffer:setStyle()
textMode(CORNER)
font( self.font )
fill(255)
fontSize( self.fontSize )
textWrapWidth(10000)
end
function Buffer:cursorToScreen(c)
-- Get a subset of the buffer
local lines = table_slice( self.buffer, 1, c.x )
local l = self.buffer[c.x]
local upToStr = self:bufferToString(lines)
local lstr = nil
if l then
lstr = table.concat(l)
end
pushStyle()
self:setStyle()
local lw = 0
local _,lh = textSize("A")
local emptyLine = true
if lstr and lstr ~= "" then
lw,lh = textSize(string.sub(lstr,1,c.y - 1))
emptyLine = false
end
local pw,ph = textSize(upToStr)
if emptyLine then
ph = ph + lh
end
popStyle()
return lw,(ph - lh/2)
end
function Buffer:bufferToString(b)
local bstrings = {}
for k,v in pairs(b) do
table.insert(bstrings, table.concat( b[k] ) )
end
return table.concat( bstrings, "\
" )
end
function Buffer:moveCursor(o)
self.cursor = self.cursor + o
self.cursor.x = math.max(1, math.min(self.cursor.x, #self.buffer))
local l = self.buffer[self.cursor.x]
local y = self.cursor.y
y = math.max(1, math.min(y, #l + 1))
self.cursor.y = y
end
function Buffer:insertCharacter(c)
local l = self.buffer[self.cursor.x]
if l == nil then
l = {}
self.buffer[self.cursor.x] = l
end
if c == "\
" then
local start = table_slice(l,1,self.cursor.y)
local tail = table_slice(l,self.cursor.y+1,#l)
table[self.cursor.x] = start
table.insert(self.buffer, self.cursor.x+1, tail)
self.cursor = vec2( self.cursor.x + 1, 1 )
elseif c == BACKSPACE then
if self.cursor.y == 1 then
-- delete line
local prevLine = self.buffer[self.cursor.x - 1]
table.remove(self.buffer, self.cursor.x)
if prevLine then
self.cursor = vec2(self.cursor.x - 1, #prevLine + 1)
table_append( prevLine, l )
end
else
-- delete character
table.remove(l, self.cursor.y - 1 )
self.cursor = vec2(self.cursor.x, self.cursor.y - 1 )
end
else
table.insert(l, self.cursor.y, c)
self.cursor = vec2( self.cursor.x, self.cursor.y + 1 )
end
end
function Buffer:draw()
self.t = self.t + 8 * DeltaTime
self.cursorBlink = (math.sin(self.t) + 1) * 128
pushStyle()
self:setStyle()
local str = self:toString()
local w,h = textSize(str)
pushMatrix()
translate( 40, -40 )
text(str, 0, HEIGHT - h)
-- Draw cursor
-- Cursor pos x,y
local cpx,cpy = self:cursorToScreen(self.cursor)
fill(0, 87, 255, self.cursorBlink)
rectMode(CENTER)
rect(cpx,HEIGHT - cpy,5,22)
popMatrix()
popStyle()
end
function Buffer:clear()
self.cursor = vec2(1,1)
self.buffer = {}
end
function Buffer:toString()
return self:bufferToString(self.buffer)
end
function Buffer:toStringWithoutActiveLine()
local bstrings = {}
for k,v in pairs(self.buffer) do
if k ~= self.cursor.x then
table.insert(bstrings, table.concat( self.buffer[k] ) )
end
end
return table.concat( bstrings, "\
" )
end
EmButton
-- Emoji Button
EmButton = class()
function EmButton:init(pos,txt)
-- you can accept and set parameters here
self.pos = pos
self.text = txt
self.action = nil
self.highlight = false
self.color = color(255,255,255,255)
end
function EmButton:size()
pushStyle()
self:setStyle()
local w,h = textSize(self.text)
popStyle()
return w,h
end
function EmButton:hitTest(lp)
local w,h = self:size()
local left,right = self.pos.x - w/2, self.pos.x + w/2
local top,bottom = self.pos.y + h/2, self.pos.y - h/2
if lp.x > left and lp.x < right and
lp.y > bottom and lp.y < top then
return true
end
return false
end
function EmButton:setStyle()
fill(self.color)
noStroke()
textMode(CENTER)
fontSize(50)
font("AppleColorEmoji")
end
function EmButton:draw()
pushMatrix()
translate(self.pos.x,self.pos.y)
pushStyle()
self:setStyle()
text(self.text,0,0)
popStyle()
popMatrix()
end
function EmButton:touched(touch)
if touch.state == ENDED then
-- Tapped
if self:hitTest( vec2(touch.x,touch.y) ) then
if self.action then self.action() end
end
end
end