* Update how text renders in button and key * Add a placeholder * Add actual code and language entry * Sort buttons * Alter to keep original behaviour * I HAVE OCD * Add back legacy behaviour
1547 lines
39 KiB
Lua
1547 lines
39 KiB
Lua
local gc=love.graphics
|
|
local gc_origin=gc.origin
|
|
local gc_translate,gc_replaceTransform=gc.translate,gc.replaceTransform
|
|
local gc_stencil,gc_setStencilTest=gc.stencil,gc.setStencilTest
|
|
local gc_push,gc_pop=gc.push,gc.pop
|
|
local gc_setCanvas,gc_setBlendMode=gc.setCanvas,gc.setBlendMode
|
|
local gc_setColor,gc_setLineWidth=gc.setColor,gc.setLineWidth
|
|
local gc_draw,gc_line=gc.draw,gc.line
|
|
local gc_rectangle=gc.rectangle
|
|
local gc_print,gc_printf=gc.print,gc.printf
|
|
|
|
local kb=love.keyboard
|
|
local timer=love.timer.getTime
|
|
|
|
local next=next
|
|
local floor,ceil=math.floor,math.ceil
|
|
local max,min=math.max,math.min
|
|
local match=string.match
|
|
local sub,ins,rem=string.sub,table.insert,table.remove
|
|
local xOy=SCR.xOy
|
|
local FONT=FONT
|
|
local mStr=GC.mStr
|
|
local approach=MATH.expApproach
|
|
|
|
local downArrowIcon=GC.DO{40,25,{'fPoly',0,0,20,25,40,0}}
|
|
local upArrowIcon=GC.DO{40,25,{'fPoly',0,25,20,0,40,25}}
|
|
local clearIcon=GC.DO{40,40,
|
|
{'fRect',16,5,8,3},
|
|
{'fRect',8,8,24,3},
|
|
{'fRect',11,14,18,21},
|
|
}
|
|
local sureIcon=GC.DO{40,40,
|
|
{'rawFT',35},
|
|
{'mText',"?",20,0},
|
|
}
|
|
local smallerThen=GC.DO{20,20,
|
|
{'setLW',5},
|
|
{'line',18,2,1,10,18,18},
|
|
}
|
|
local largerThen=GC.DO{20,20,
|
|
{'setLW',5},
|
|
{'line',2,2,19,10,2,18},
|
|
}
|
|
|
|
local STW,STH-- stencil-wid/hei
|
|
local function _rectangleStencil()
|
|
gc.rectangle('fill',1,1,STW-2,STH-2)
|
|
end
|
|
|
|
local onChange=NULL
|
|
|
|
local WIDGET={}
|
|
|
|
function WIDGET.setOnChange(func) onChange=assert(type(func)=='function' and func,"WIDGET.setOnChange(func): func must be function") end
|
|
|
|
local widgetMetatable={
|
|
__tostring=function(self)
|
|
return self:getInfo()
|
|
end,
|
|
}
|
|
|
|
local text={
|
|
type='text',
|
|
mustHaveText=true,
|
|
alpha=0,
|
|
}
|
|
|
|
function text:reset() end
|
|
function text:update(dt)
|
|
if self.hideF and self.hideF() then
|
|
if self.alpha>0 then
|
|
self.alpha=max(self.alpha-dt*7.5,0)
|
|
end
|
|
elseif self.alpha<1 then
|
|
self.alpha=min(self.alpha+dt*7.5,1)
|
|
end
|
|
end
|
|
function text:draw()
|
|
if self.alpha>0 then
|
|
local c=self.color
|
|
gc_setColor(c[1],c[2],c[3],self.alpha)
|
|
local w=self.obj:getWidth()
|
|
local k=min(self.lim/self.obj:getWidth(),1)
|
|
if self.align=='M' then
|
|
gc_draw(self.obj,self.x,self.y,nil,k,1,w*.5,0)
|
|
elseif self.align=='L' then
|
|
gc_draw(self.obj,self.x,self.y,nil,k,1)
|
|
elseif self.align=='R' then
|
|
gc_draw(self.obj,self.x,self.y,nil,k,1,w,0)
|
|
end
|
|
end
|
|
end
|
|
function WIDGET.newText(D)-- name,x,y[,lim][,fText][,color][,font=30][,fType][,align='M'][,hideF][,hide]
|
|
local _={
|
|
name= D.name or "_",
|
|
x= D.x,
|
|
y= D.y,
|
|
lim= D.lim or 1e99,
|
|
|
|
fText=D.fText,
|
|
color=D.color and (COLOR[D.color] or D.color) or COLOR.Z,
|
|
font= D.font or 30,
|
|
fType=D.fType,
|
|
align=D.align or 'M',
|
|
hideF=D.hideF,
|
|
}
|
|
for k,v in next,text do _[k]=v end
|
|
if not _.hideF then _.alpha=1 end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
local image={
|
|
type='image',
|
|
}
|
|
function image:reset()
|
|
if type(self.img)=='string' then
|
|
self.img=IMG[self.img]
|
|
end
|
|
end
|
|
function image:draw()
|
|
gc_setColor(1,1,1,self.alpha)
|
|
gc_draw(self.img,self.x,self.y,self.ang,self.k)
|
|
end
|
|
function WIDGET.newImage(D)-- name[,img(name)],x,y[,ang][,k][,hideF][,hide]
|
|
local _={
|
|
name= D.name or "_",
|
|
img= D.img or D.name or "_",
|
|
alpha=D.alpha,
|
|
x= D.x,
|
|
y= D.y,
|
|
ang= D.ang,
|
|
k= D.k,
|
|
hideF=D.hideF,
|
|
hide= D.hide,
|
|
}
|
|
for k,v in next,image do _[k]=v end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
local button={
|
|
type='button',
|
|
mustHaveText=true,
|
|
ATV=0,-- Activating time(0~8)
|
|
textAlreadyWrapped=false,-- Text already wrapped? (Managed by :setObject, can be override, this will be true if obj has a '\n')
|
|
}
|
|
function button:reset()
|
|
self.ATV=0
|
|
end
|
|
function button:setObject(obj)
|
|
if type(obj)=='string' or type(obj)=='number' then
|
|
if match(obj,"\n") then
|
|
self.textAlreadyWrapped=true
|
|
self.obj=gc.newText(FONT.get(self.font,self.fType))
|
|
self.obj:addf(obj,self.w-self.edge*2,(self.align=='L' and 'left') or (self.align=='R' and 'right') or 'center')
|
|
else
|
|
self.textAlreadyWrapped=false
|
|
self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
|
|
end
|
|
elseif obj then
|
|
self.obj=obj
|
|
end
|
|
end
|
|
function button:isAbove(x,y)
|
|
local ATV=self.ATV
|
|
return
|
|
x>self.x-ATV and
|
|
y>self.y and
|
|
x<self.x+self.w+2*ATV and
|
|
y<self.y+self.h
|
|
end
|
|
function button:getCenter()
|
|
return self.x+self.w*.5,self.y+self.h*.5
|
|
end
|
|
function button:update(dt)
|
|
local ATV=self.ATV
|
|
if WIDGET.sel==self then
|
|
if ATV<8 then self.ATV=min(ATV+dt*60,8) end
|
|
else
|
|
if ATV>0 then self.ATV=max(ATV-dt*30,0) end
|
|
end
|
|
end
|
|
function button:draw()
|
|
local x,y,w,h=self.x,self.y,self.w,self.h
|
|
local ATV=self.ATV
|
|
local c=self.color
|
|
local r,g,b=c[1],c[2],c[3]
|
|
|
|
-- Button
|
|
gc_setColor(.15+r*.7,.15+g*.7,.15+b*.7,.9)
|
|
gc_rectangle('fill',x-ATV,y,w+2*ATV,h,4)
|
|
gc_setLineWidth(2)
|
|
gc_setColor(.3+r*.7,.3+g*.7,.3+b*.7)
|
|
gc_rectangle('line',x-ATV,y,w+2*ATV,h,5)
|
|
if ATV>0 then
|
|
gc_setColor(.97,.97,.97,ATV*.125)
|
|
gc_rectangle('line',x-ATV,y,w+2*ATV,h,3)
|
|
end
|
|
|
|
-- Drawable
|
|
local obj=self.obj
|
|
local ox,oy=obj:getWidth()*.5,obj:getHeight()*.5
|
|
local y0=y+h*.5
|
|
gc_setColor(1,1,1,.2+ATV*.05)
|
|
if self.align=='L' or self.textAlreadyWrapped then
|
|
local edge=self.edge
|
|
gc_draw(obj,x+edge-1,y0-1-oy)
|
|
gc_draw(obj,x+edge-1,y0+1-oy)
|
|
gc_draw(obj,x+edge+1,y0-1-oy)
|
|
gc_draw(obj,x+edge+1,y0+1-oy)
|
|
gc_setColor(r*.55,g*.55,b*.55)
|
|
gc_draw(obj,x+edge,y0-oy)
|
|
elseif self.align=='R' then
|
|
local x0=x+w-self.edge-ox*2
|
|
gc_draw(obj,x0-1,y0-1-oy)
|
|
gc_draw(obj,x0-1,y0+1-oy)
|
|
gc_draw(obj,x0+1,y0-1-oy)
|
|
gc_draw(obj,x0+1,y0+1-oy)
|
|
gc_setColor(r*.55,g*.55,b*.55)
|
|
gc_draw(obj,x0,y0-oy)
|
|
else--if self.align=='M' then
|
|
local x0=x+w*.5
|
|
local kx=obj:type()=='Text' and min(w/ox/2,1) or 1
|
|
gc_draw(obj,x0-1,y0-1,nil,kx,1,ox,oy)
|
|
gc_draw(obj,x0-1,y0+1,nil,kx,1,ox,oy)
|
|
gc_draw(obj,x0+1,y0-1,nil,kx,1,ox,oy)
|
|
gc_draw(obj,x0+1,y0+1,nil,kx,1,ox,oy)
|
|
gc_setColor(r*.55,g*.55,b*.55)
|
|
gc_draw(obj,x0,y0,nil,kx,1,ox,oy)
|
|
end
|
|
end
|
|
function button:getInfo()
|
|
return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font,self.fType)
|
|
end
|
|
function button:press(_,_,k)
|
|
end
|
|
function button:drag(x,y)
|
|
if not self:isAbove(x,y) and self==WIDGET.sel then
|
|
WIDGET.unFocus()
|
|
end
|
|
end
|
|
function button:release(_,_,k)
|
|
self.code(k)
|
|
local ATV=self.ATV
|
|
SYSFX.newRectRipple(
|
|
6,
|
|
self.x-ATV,
|
|
self.y-WIDGET.scrollPos,
|
|
self.w+2*ATV,
|
|
self.h
|
|
)
|
|
if self.sound then
|
|
SFX.play(self.sound)
|
|
end
|
|
end
|
|
function WIDGET.newButton(D)-- name,x,y,w[,h][,fText][,color][,font=30][,fType][,sound][,align='M'][,edge=0][,code][,hideF][,hide]
|
|
if not D.h then D.h=D.w end
|
|
local w={
|
|
name= D.name or "_",
|
|
|
|
x= D.x-D.w*.5,
|
|
y= D.y-D.h*.5,
|
|
w= D.w,
|
|
h= D.h,
|
|
|
|
resCtr={
|
|
D.x,D.y,
|
|
D.x-D.w*.35,D.y-D.h*.35,
|
|
D.x-D.w*.35,D.y+D.h*.35,
|
|
D.x+D.w*.35,D.y-D.h*.35,
|
|
D.x+D.w*.35,D.y+D.h*.35,
|
|
},
|
|
|
|
fText=D.fText,
|
|
color=D.color and (COLOR[D.color] or D.color) or COLOR.Z,
|
|
font= D.font or 30,
|
|
fType=D.fType,
|
|
align=D.align or 'M',
|
|
edge= D.edge or 0,
|
|
code= D.code or NULL,
|
|
hideF=D.hideF,
|
|
hide= D.hide,
|
|
}
|
|
if D.sound==false then
|
|
w.sound=false
|
|
elseif type(D.sound)=='string' then
|
|
w.sound=D.sound
|
|
else
|
|
w.sound='button'
|
|
end
|
|
|
|
for k,v in next,button do w[k]=v end
|
|
setmetatable(w,widgetMetatable)
|
|
return w
|
|
end
|
|
|
|
local key={
|
|
type='key',
|
|
mustHaveText=true,
|
|
ATV=0,-- Activating time(0~4)
|
|
textAlreadyWrapped=false,---See button.setObject (line 146)
|
|
}
|
|
function key:reset()
|
|
self.ATV=0
|
|
end
|
|
function key:setObject(obj)
|
|
if type(obj)=='string' or type(obj)=='number' then
|
|
if match(obj,"\n") then
|
|
self.textAlreadyWrapped=true
|
|
self.obj=gc.newText(FONT.get(self.font,self.fType))
|
|
self.obj:addf(obj,self.w-self.edge*2,(self.align=='L' and 'left') or (self.align=='R' and 'right') or 'center')
|
|
else
|
|
self.textAlreadyWrapped=false
|
|
self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
|
|
end
|
|
elseif obj then
|
|
self.obj=obj
|
|
end
|
|
end
|
|
function key:isAbove(x,y)
|
|
return
|
|
x>self.x and
|
|
y>self.y and
|
|
x<self.x+self.w and
|
|
y<self.y+self.h
|
|
end
|
|
function key:getCenter()
|
|
return self.x+self.w*.5,self.y+self.h*.5
|
|
end
|
|
function key:update(dt)
|
|
local ATV=self.ATV
|
|
if WIDGET.sel==self then
|
|
if ATV<4 then self.ATV=min(ATV+dt*60,4) end
|
|
else
|
|
if ATV>0 then self.ATV=max(ATV-dt*30,0) end
|
|
end
|
|
end
|
|
function key:draw()
|
|
local x,y,w,h=self.x,self.y,self.w,self.h
|
|
local ATV=self.ATV
|
|
local c=self.color
|
|
local align=self.align
|
|
local r,g,b=c[1],c[2],c[3]
|
|
|
|
-- Fill
|
|
if self.fShade then
|
|
gc_setColor(r,g,b,ATV*.25)
|
|
if align=='M' then
|
|
gc_draw(self.fShade,x+w*.5-self.fShade:getWidth()*.5,y+h*.5-self.fShade:getHeight()*.5)
|
|
elseif align=='L' then
|
|
gc_draw(self.fShade,x+self.edge,y+h*.5-self.fShade:getHeight()*.5)
|
|
elseif align=='R' then
|
|
gc_draw(self.fShade,x+w-self.edge-self.fShade:getWidth(),y+h*.5-self.fShade:getHeight()*.5)
|
|
end
|
|
else
|
|
-- Background
|
|
gc_setColor(0,0,0,.3)
|
|
gc_rectangle('fill',x,y,w,h,4)
|
|
|
|
-- Frame
|
|
gc_setColor(.2+r*.8,.2+g*.8,.2+b*.8,.7)
|
|
gc_setLineWidth(2)
|
|
gc_rectangle('line',x,y,w,h,3)
|
|
|
|
-- Shade
|
|
gc_setColor(1,1,1,ATV*.05)
|
|
gc_rectangle('fill',x,y,w,h,3)
|
|
end
|
|
|
|
-- Drawable
|
|
local obj=self.obj
|
|
local ox,oy=obj:getWidth()*.5,obj:getHeight()*.5
|
|
|
|
gc_setColor(r,g,b)
|
|
if align=='L' or self.textAlreadyWrapped then
|
|
gc_draw(obj,x+self.edge,y+h*.5-oy)
|
|
elseif align=='R' then
|
|
gc_draw(obj,x+w-self.edge-ox*2,y-oy+h*.5)
|
|
else--if align=='M' then
|
|
local kx=obj:type()=='Text' and min(w/ox/2,1) or 1
|
|
gc_draw(obj,x+w*.5,y+h*.5,nil,kx,1,ox,oy)
|
|
end
|
|
end
|
|
function key:getInfo()
|
|
return("x=%d,y=%d,w=%d,h=%d,font=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h,self.font,self.fType)
|
|
end
|
|
function key:press()
|
|
end
|
|
function key:drag(x,y)
|
|
if not self:isAbove(x,y) and self==WIDGET.sel then
|
|
WIDGET.unFocus()
|
|
end
|
|
end
|
|
function key:release(_,_,k)
|
|
self.code(k)
|
|
if self.sound then
|
|
SFX.play(self.sound)
|
|
end
|
|
end
|
|
function WIDGET.newKey(D)-- name,x,y,w[,h][,fText][,fShade][,color][,font=30][,fType][,sound][,align='M'][,edge=0][,code][,hideF][,hide]
|
|
if not D.h then D.h=D.w end
|
|
local _={
|
|
name= D.name or "_",
|
|
|
|
x= D.x-D.w*.5,
|
|
y= D.y-D.h*.5,
|
|
w= D.w,
|
|
h= D.h,
|
|
|
|
resCtr={
|
|
D.x,D.y,
|
|
D.x-D.w*.35,D.y-D.h*.35,
|
|
D.x-D.w*.35,D.y+D.h*.35,
|
|
D.x+D.w*.35,D.y-D.h*.35,
|
|
D.x+D.w*.35,D.y+D.h*.35,
|
|
},
|
|
|
|
fText= D.fText,
|
|
fShade= D.fShade,
|
|
color= D.color and (COLOR[D.color] or D.color) or COLOR.Z,
|
|
font= D.font or 30,
|
|
fType= D.fType,
|
|
align= D.align or 'M',
|
|
edge= D.edge or 0,
|
|
code= D.code or NULL,
|
|
hideF= D.hideF,
|
|
hide= D.hide,
|
|
}
|
|
if D.sound==false then
|
|
_.sound=false
|
|
elseif type(D.sound)=='string' then
|
|
_.sound=D.sound
|
|
else
|
|
_.sound='key'
|
|
end
|
|
for k,v in next,key do _[k]=v end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
local switch={
|
|
type='switch',
|
|
mustHaveText=true,
|
|
ATV=0,-- Activating time(0~8)
|
|
CHK=0,-- Check alpha(0~6)
|
|
}
|
|
function switch:reset()
|
|
self.ATV=0
|
|
self.CHK=0
|
|
end
|
|
function switch:isAbove(x,y)
|
|
return x>self.x and x<self.x+50 and y>self.y-25 and y<self.y+25
|
|
end
|
|
function switch:getCenter()
|
|
return self.x,self.y
|
|
end
|
|
function switch:update(dt)
|
|
local ATV=self.ATV
|
|
if WIDGET.sel==self then
|
|
if ATV<8 then self.ATV=min(ATV+dt*60,8) end
|
|
else
|
|
if ATV>0 then self.ATV=max(ATV-dt*30,0) end
|
|
end
|
|
local chk=self.CHK
|
|
if self:disp() then
|
|
if chk<6 then self.CHK=min(chk+dt*60,6) end
|
|
else
|
|
if chk>0 then self.CHK=max(chk-dt*60,0) end
|
|
end
|
|
end
|
|
function switch:draw()
|
|
local x,y=self.x,self.y
|
|
local ATV=self.ATV
|
|
|
|
-- Background
|
|
gc_setColor(0,0,0,.3)
|
|
gc_rectangle('fill',x,y-25,50,50,4)
|
|
|
|
-- Frame
|
|
gc_setLineWidth(2)
|
|
gc_setColor(1,1,1,.6+ATV*.1)
|
|
gc_rectangle('line',x,y-25,50,50,3)
|
|
|
|
-- Checked
|
|
if ATV>0 then
|
|
gc_setColor(1,1,1,ATV*.06)
|
|
gc_rectangle('fill',x,y-25,50,50,3)
|
|
end
|
|
if self.CHK>0 then
|
|
gc_setColor(.9,1,.9,self.CHK/6)
|
|
gc_setLineWidth(5)
|
|
gc_line(x+5,y,x+18,y+13,x+45,y-14)
|
|
end
|
|
|
|
-- Drawable
|
|
local obj=self.obj
|
|
gc_setColor(self.color)
|
|
gc_draw(obj,x-12-ATV,y,nil,min(self.lim/obj:getWidth(),1),1,obj:getWidth(),obj:getHeight()*.5)
|
|
end
|
|
function switch:getInfo()
|
|
return("x=%d,y=%d,font=%d"):format(self.x,self.y,self.font,self.fType)
|
|
end
|
|
function switch:press()
|
|
self.code()
|
|
if self.sound then
|
|
SFX.play(self.disp() and 'check' or 'uncheck')
|
|
end
|
|
end
|
|
function WIDGET.newSwitch(D)-- name,x,y[,lim][,fText][,color][,font=30][,fType][,sound=true][,disp][,code][,hideF][,hide]
|
|
local _={
|
|
name= D.name or "_",
|
|
|
|
x= D.x,
|
|
y= D.y,
|
|
lim= D.lim or 1e99,
|
|
|
|
resCtr={
|
|
D.x+25,D.y,
|
|
},
|
|
|
|
fText=D.fText,
|
|
color=D.color and (COLOR[D.color] or D.color) or COLOR.Z,
|
|
font= D.font or 30,
|
|
fType=D.fType,
|
|
sound=D.sound~=false,
|
|
disp= D.disp,
|
|
code= D.code or NULL,
|
|
hideF=D.hideF,
|
|
hide= D.hide,
|
|
}
|
|
for k,v in next,switch do _[k]=v end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
local slider={
|
|
type='slider',
|
|
ATV=0,-- Activating time(0~8)
|
|
TAT=0,-- Text activating time(0~180)
|
|
pos=0,-- Position shown
|
|
lastTime=0,-- Last value changing time
|
|
}
|
|
local sliderShowFunc={
|
|
int=function(S)
|
|
return S.disp()
|
|
end,
|
|
float=function(S)
|
|
return floor(S.disp()*100+.5)*.01
|
|
end,
|
|
percent=function(S)
|
|
return floor(S.disp()*100+.5).."%"
|
|
end,
|
|
}
|
|
function slider:reset()
|
|
self.ATV=0
|
|
self.TAT=180
|
|
self.pos=0
|
|
end
|
|
function slider:isAbove(x,y)
|
|
return x>self.x-10 and x<self.x+self.w+10 and y>self.y-25 and y<self.y+25
|
|
end
|
|
function slider:getCenter()
|
|
return self.x+self.w*((self.pos-self.rangeL)/(self.rangeR-self.rangeL)),self.y
|
|
end
|
|
function slider:update(dt)
|
|
local ATV=self.ATV
|
|
if self.TAT>0 then
|
|
self.TAT=max(self.TAT-dt*60,0)
|
|
end
|
|
if WIDGET.sel==self then
|
|
if ATV<6 then self.ATV=min(ATV+dt*60,6) end
|
|
self.TAT=180
|
|
else
|
|
if ATV>0 then self.ATV=max(ATV-dt*30,0) end
|
|
end
|
|
if not self.hide then
|
|
self.pos=approach(self.pos,self.disp(),dt*26)
|
|
end
|
|
end
|
|
function slider:draw()
|
|
local x,y=self.x,self.y
|
|
local ATV=self.ATV
|
|
local x2=x+self.w
|
|
|
|
gc_setColor(1,1,1,.5+ATV*.06)
|
|
|
|
-- Units
|
|
if not self.smooth then
|
|
gc_setLineWidth(2)
|
|
for p=self.rangeL,self.rangeR,self.unit do
|
|
local X=x+(x2-x)*(p-self.rangeL)/(self.rangeR-self.rangeL)
|
|
gc_line(X,y+7,X,y-7)
|
|
end
|
|
end
|
|
|
|
-- Axis
|
|
gc_setLineWidth(4)
|
|
gc_line(x,y,x2,y)
|
|
|
|
-- Block
|
|
local cx=x+(x2-x)*(self.pos-self.rangeL)/(self.rangeR-self.rangeL)
|
|
local bx,by,bw,bh=cx-10-ATV*.5,y-16-ATV,20+ATV,32+2*ATV
|
|
gc_setColor(.8,.8,.8)
|
|
gc_rectangle('fill',bx,by,bw,bh,3)
|
|
|
|
-- Glow
|
|
if ATV>0 then
|
|
gc_setLineWidth(2)
|
|
gc_setColor(.97,.97,.97,ATV*.16)
|
|
gc_rectangle('line',bx+1,by+1,bw-2,bh-2,3)
|
|
end
|
|
|
|
-- Float text
|
|
if self.TAT>0 and self.show then
|
|
FONT.set(25)
|
|
gc_setColor(.97,.97,.97,self.TAT/180)
|
|
mStr(self:show(),cx,by-30)
|
|
end
|
|
|
|
-- Drawable
|
|
local obj=self.obj
|
|
if obj then
|
|
gc_setColor(self.color)
|
|
gc_draw(obj,x-12-ATV,y,nil,min(self.lim/obj:getWidth(),1),1,obj:getWidth(),obj:getHeight()*.5)
|
|
end
|
|
end
|
|
function slider:getInfo()
|
|
return("x=%d,y=%d,w=%d"):format(self.x,self.y,self.w)
|
|
end
|
|
function slider:press(x)
|
|
self:drag(x)
|
|
end
|
|
function slider:drag(x)
|
|
if not x then return end
|
|
x=x-self.x
|
|
local newPos=MATH.clamp(x/self.w,0,1)
|
|
local newVal
|
|
if not self.unit then
|
|
newVal=(1-newPos)*self.rangeL+newPos*self.rangeR
|
|
else
|
|
newVal=newPos*(self.rangeR-self.rangeL)
|
|
newVal=self.rangeL+floor(newVal/self.unit+.5)*self.unit
|
|
end
|
|
if newVal~=self.disp() then
|
|
self.code(newVal)
|
|
end
|
|
if self.change and timer()-self.lastTime>.5 then
|
|
self.lastTime=timer()
|
|
self.change()
|
|
end
|
|
end
|
|
function slider:release(x)
|
|
self:drag(x)
|
|
self.lastTime=0
|
|
end
|
|
function slider:scroll(n)
|
|
local p=self.disp()
|
|
local u=self.unit or .01
|
|
local P=MATH.clamp(p+u*n,self.rangeL,self.rangeR)
|
|
if p==P or not P then return end
|
|
self.code(P)
|
|
if self.change and timer()-self.lastTime>.18 then
|
|
self.lastTime=timer()
|
|
self.change()
|
|
end
|
|
end
|
|
function slider:arrowKey(k)
|
|
self:scroll((k=='left' or k=='up') and -1 or 1)
|
|
end
|
|
function WIDGET.newSlider(D)-- name,x,y,w[,lim][,fText][,color][,axis][,smooth][,font=30][,fType][,change],disp[,show][,code],hide
|
|
if not D.axis then
|
|
D.axis={0,1,false}
|
|
D.smooth=true
|
|
elseif not D.axis[3] then
|
|
D.smooth=true
|
|
end
|
|
local _={
|
|
name= D.name or "_",
|
|
|
|
x= D.x,
|
|
y= D.y,
|
|
w= D.w,
|
|
lim= D.lim or 1e99,
|
|
|
|
resCtr={
|
|
D.x,D.y,
|
|
D.x+D.w*.25,D.y,
|
|
D.x+D.w*.5,D.y,
|
|
D.x+D.w*.75,D.y,
|
|
D.x+D.w,D.y,
|
|
},
|
|
|
|
fText= D.fText,
|
|
color= D.color and (COLOR[D.color] or D.color) or COLOR.Z,
|
|
rangeL=D.axis[1],
|
|
rangeR=D.axis[2],
|
|
unit= D.axis[3],
|
|
smooth=D.smooth,
|
|
font= D.font or 30,
|
|
fType= D.fType,
|
|
change=D.change,
|
|
disp= D.disp,
|
|
code= D.code or NULL,
|
|
hideF= D.hideF,
|
|
hide= D.hide,
|
|
show= false,
|
|
}
|
|
if D.show then
|
|
if type(D.show)=='function' then
|
|
_.show=D.show
|
|
else
|
|
_.show=sliderShowFunc[D.show]
|
|
end
|
|
elseif D.show~=false then-- Use default if nil
|
|
if _.unit and _.unit%1==0 then
|
|
_.show=sliderShowFunc.int
|
|
else
|
|
_.show=sliderShowFunc.percent
|
|
end
|
|
end
|
|
for k,v in next,slider do _[k]=v end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
local selector={
|
|
type='selector',
|
|
mustHaveText=true,
|
|
ATV=8,-- Activating time(0~4)
|
|
select=false,-- Selected item ID
|
|
selText=false,-- Selected item name
|
|
}
|
|
function selector:reset()
|
|
self.ATV=0
|
|
local V,L=self.disp(),self.list
|
|
for i=1,#L do
|
|
if L[i]==V then
|
|
self.select=i
|
|
self.selText=self.list[i]
|
|
return
|
|
end
|
|
end
|
|
self.select=0
|
|
self.selText=""
|
|
MES.new('error',"Selector "..self.name.." dead, disp= "..tostring(V))
|
|
end
|
|
function selector:isAbove(x,y)
|
|
return
|
|
x>self.x and
|
|
x<self.x+self.w+2 and
|
|
y>self.y and
|
|
y<self.y+60
|
|
end
|
|
function selector:getCenter()
|
|
return self.x+self.w*.5,self.y+30
|
|
end
|
|
function selector:update(dt)
|
|
local ATV=self.ATV
|
|
if WIDGET.sel==self then
|
|
if ATV<8 then self.ATV=min(ATV+dt*60,8) end
|
|
else
|
|
if ATV>0 then self.ATV=max(ATV-dt*30,0) end
|
|
end
|
|
end
|
|
function selector:draw()
|
|
local x,y=self.x,self.y
|
|
local w=self.w
|
|
local ATV=self.ATV
|
|
|
|
-- Background
|
|
gc_setColor(0,0,0,.3)
|
|
gc_rectangle('fill',x,y,w,60,4)
|
|
|
|
-- Frame
|
|
gc_setColor(1,1,1,.6+ATV*.1)
|
|
gc_setLineWidth(2)
|
|
gc_rectangle('line',x,y,w,60,3)
|
|
|
|
-- Arrow
|
|
gc_setColor(1,1,1,.2+ATV*.1)
|
|
local t=(timer()%.5)^.5
|
|
if self.select>1 then
|
|
gc_draw(smallerThen,x+6,y+33)
|
|
if ATV>0 then
|
|
gc_setColor(1,1,1,ATV*.4*(.5-t))
|
|
gc_draw(smallerThen,x+6-t*40,y+33)
|
|
gc_setColor(1,1,1,.2+ATV*.1)
|
|
end
|
|
end
|
|
if self.select<#self.list then
|
|
gc_draw(largerThen,x+w-26,y+33)
|
|
if ATV>0 then
|
|
gc_setColor(1,1,1,ATV*.4*(.5-t))
|
|
gc_draw(largerThen,x+w-26+t*40,y+33)
|
|
end
|
|
end
|
|
|
|
-- Drawable
|
|
gc_setColor(self.color)
|
|
gc_draw(self.obj,x+w*.5,y-4,nil,min((w-20)/self.obj:getWidth(),1),1,self.obj:getWidth()*.5,0)
|
|
gc_setColor(1,1,1)
|
|
FONT.set(30)
|
|
mStr(self.selText,x+w*.5,y+22)
|
|
end
|
|
function selector:getInfo()
|
|
return("x=%d,y=%d,w=%d"):format(self.x+self.w*.5,self.y+30,self.w)
|
|
end
|
|
function selector:press(x)
|
|
if x then
|
|
local s=self.select
|
|
if x<self.x+self.w*.5 then
|
|
if s>1 then
|
|
s=s-1
|
|
SYSFX.newShade(3,self.x,self.y-WIDGET.scrollPos,self.w*.5,60)
|
|
end
|
|
else
|
|
if s<#self.list then
|
|
s=s+1
|
|
SYSFX.newShade(3,self.x+self.w*.5,self.y-WIDGET.scrollPos,self.w*.5,60)
|
|
end
|
|
end
|
|
if self.select~=s then
|
|
self.code(self.list[s],s)
|
|
self.select=s
|
|
self.selText=self.list[s]
|
|
if self.sound then
|
|
SFX.play('selector')
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function selector:scroll(n)
|
|
local s=self.select
|
|
if n==-1 then
|
|
if s==1 then return end
|
|
s=s-1
|
|
SYSFX.newShade(3,self.x,self.y-WIDGET.scrollPos,self.w*.5,60)
|
|
else
|
|
if s==#self.list then return end
|
|
s=s+1
|
|
SYSFX.newShade(3,self.x+self.w*.5,self.y-WIDGET.scrollPos,self.w*.5,60)
|
|
end
|
|
self.code(self.list[s])
|
|
self.select=s
|
|
self.selText=self.list[s]
|
|
if self.sound then
|
|
SFX.play('selector')
|
|
end
|
|
end
|
|
function selector:arrowKey(k)
|
|
self:scroll((k=='left' or k=='up') and -1 or 1)
|
|
end
|
|
|
|
function WIDGET.newSelector(D)-- name,x,y,w[,fText][,color][,sound=true],list,disp[,code],hide
|
|
local _={
|
|
name= D.name or "_",
|
|
|
|
x= D.x-D.w*.5,
|
|
y= D.y-30,
|
|
w= D.w,
|
|
|
|
resCtr={
|
|
D.x,D.y,
|
|
D.x+D.w*.25,D.y,
|
|
D.x+D.w*.5,D.y,
|
|
D.x+D.w*.75,D.y,
|
|
D.x+D.w,D.y,
|
|
},
|
|
|
|
fText=D.fText,
|
|
color=D.color and (COLOR[D.color] or D.color) or COLOR.Z,
|
|
sound=D.sound~=false,
|
|
font= 30,
|
|
list= D.list,
|
|
disp= D.disp,
|
|
code= D.code or NULL,
|
|
hideF=D.hideF,
|
|
hide= D.hide,
|
|
}
|
|
for k,v in next,selector do _[k]=v end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
local inputBox={
|
|
type='inputBox',
|
|
keepFocus=true,
|
|
ATV=0,-- Activating time(0~4)
|
|
value="",-- Text contained
|
|
}
|
|
function inputBox:reset()
|
|
self.ATV=0
|
|
end
|
|
function inputBox:hasText()
|
|
return #self.value>0
|
|
end
|
|
function inputBox:getText()
|
|
return self.value
|
|
end
|
|
function inputBox:setText(str)
|
|
if not str then str="" end
|
|
assert(type(str)=='string',"Arg #1 must be string")
|
|
self.value=str
|
|
end
|
|
function inputBox:addText(str)
|
|
if not str then str="" end
|
|
assert(type(str)=='string',"Arg #1 must be string")
|
|
self.value=self.value..str
|
|
end
|
|
function inputBox:clear()
|
|
self.value=""
|
|
end
|
|
function inputBox:isAbove(x,y)
|
|
return
|
|
x>self.x and
|
|
y>self.y and
|
|
x<self.x+self.w and
|
|
y<self.y+self.h
|
|
end
|
|
function inputBox:getCenter()
|
|
return self.x+self.w*.5,self.y
|
|
end
|
|
function inputBox:update(dt)
|
|
local ATV=self.ATV
|
|
if WIDGET.sel==self then
|
|
if ATV<3 then self.ATV=min(ATV+dt*60,3) end
|
|
else
|
|
if ATV>0 then self.ATV=max(ATV-dt*15,0) end
|
|
end
|
|
end
|
|
function inputBox:draw()
|
|
local x,y,w,h=self.x,self.y,self.w,self.h
|
|
local ATV=self.ATV
|
|
|
|
-- Background
|
|
gc_setColor(0,0,0,.4)
|
|
gc_rectangle('fill',x,y,w,h,4)
|
|
|
|
-- Highlight
|
|
gc_setColor(1,1,1,ATV*.08*(math.sin(timer()*4.2)*.2+.8))
|
|
gc_rectangle('fill',x,y,w,h,4)
|
|
|
|
-- Frame
|
|
gc_setColor(1,1,1)
|
|
gc_setLineWidth(3)
|
|
gc_rectangle('line',x,y,w,h,3)
|
|
|
|
-- Drawable
|
|
local f=self.font
|
|
FONT.set(f,self.fType)
|
|
if self.obj then
|
|
gc_draw(self.obj,x-12-self.obj:getWidth(),y+h*.5-self.obj:getHeight()*.5)
|
|
end
|
|
if self.secret then
|
|
y=y+h*.5-f*.2
|
|
for i=1,#self.value do
|
|
gc_rectangle("fill",x+f*.6*i,y,f*.4,f*.4)
|
|
end
|
|
else
|
|
gc_printf(self.value,x+10,y,self.w)
|
|
FONT.set(f-10)
|
|
if WIDGET.sel==self then
|
|
gc_print(EDITING,x+10,y+12-f*1.4)
|
|
end
|
|
end
|
|
end
|
|
function inputBox:getInfo()
|
|
return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h)
|
|
end
|
|
function inputBox:keypress(k)
|
|
local t=self.value
|
|
if #t>0 and EDITING=="" then
|
|
if k=='backspace' then
|
|
local p=#t
|
|
while t:byte(p)>=128 and t:byte(p)<192 do
|
|
p=p-1
|
|
end
|
|
t=sub(t,1,p-1)
|
|
SFX.play('lock')
|
|
elseif k=='delete' then
|
|
t=""
|
|
SFX.play('hold')
|
|
end
|
|
self.value=t
|
|
end
|
|
end
|
|
function WIDGET.newInputBox(D)-- name,x,y,w[,h][,font=30][,fType][,secret][,regex][,limit],hide
|
|
local _={
|
|
name= D.name or "_",
|
|
|
|
x= D.x,
|
|
y= D.y,
|
|
w= D.w,
|
|
h= D.h,
|
|
|
|
resCtr={
|
|
D.x+D.w*.2,D.y,
|
|
D.x+D.w*.5,D.y,
|
|
D.x+D.w*.8,D.y,
|
|
},
|
|
|
|
font= D.font or floor(D.h/7-1)*5,
|
|
fType= D.fType,
|
|
secret=D.secret==true,
|
|
regex= D.regex,
|
|
limit= D.limit,
|
|
hideF= D.hideF,
|
|
hide= D.hide,
|
|
}
|
|
for k,v in next,inputBox do _[k]=v end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
local textBox={
|
|
type='textBox',
|
|
scrollPos=0,-- Scroll-down-distance
|
|
sure=0,-- Sure-timer for clear history
|
|
}
|
|
function textBox:reset()
|
|
self.lineH=self.font*7/5
|
|
self.capacity=ceil((self.h-10)/self.lineH)
|
|
end
|
|
function textBox:setTexts(t)
|
|
assert(type(t)=='table',"Arg #1 must be table")
|
|
TABLE.clear(self.texts)
|
|
TABLE.connect(self.texts,t)
|
|
self.scrollPos=0
|
|
end
|
|
function textBox:clear()
|
|
self.texts={}
|
|
self.scrollPos=0
|
|
SFX.play('fall')
|
|
end
|
|
function textBox:isAbove(x,y)
|
|
return
|
|
x>self.x and
|
|
y>self.y and
|
|
x<self.x+self.w and
|
|
y<self.y+self.h
|
|
end
|
|
function textBox:getCenter()
|
|
return self.x+self.w*.5,self.y+self.w
|
|
end
|
|
function textBox:update(dt)
|
|
if self.sure>0 then
|
|
self.sure=max(self.sure-dt,0)
|
|
end
|
|
end
|
|
function textBox:push(t)
|
|
ins(self.texts,t)
|
|
if self.scrollPos>(#self.texts-1.5)*self.lineH-self.h then-- minus 1 for the new message
|
|
self.scrollPos=max(0,min(self.scrollPos+self.lineH,#self.texts*self.lineH-self.h))
|
|
end
|
|
end
|
|
function textBox:press(x,y)
|
|
if not (x and y) then return end
|
|
self:drag(0,0,0,0)
|
|
if not self.fix and x>self.x+self.w-40 and y<self.y+40 then
|
|
if self.sure>0 then
|
|
self:clear()
|
|
self.sure=0
|
|
else
|
|
self.sure=1
|
|
end
|
|
end
|
|
end
|
|
function textBox:drag(_,_,_,dy)
|
|
self.scrollPos=max(0,min(self.scrollPos-dy,#self.texts*self.lineH-self.h))
|
|
end
|
|
function textBox:scroll(dir)
|
|
if type(dir)=='string' then
|
|
if dir=="up" then
|
|
dir=-1
|
|
elseif dir=="down" then
|
|
dir=1
|
|
else
|
|
return
|
|
end
|
|
end
|
|
self:drag(nil,nil,nil,-dir*self.lineH)
|
|
end
|
|
function textBox:arrowKey(k)
|
|
if k=='up' then
|
|
self:scroll(-1)
|
|
elseif k=='down' then
|
|
self:scroll(-1)
|
|
end
|
|
end
|
|
function textBox:draw()
|
|
local x,y,w,h=self.x,self.y,self.w,self.h
|
|
local list=self.texts
|
|
local scrollPos=self.scrollPos
|
|
local lineH=self.lineH
|
|
local H=#list*lineH
|
|
|
|
-- Background
|
|
gc_setColor(0,0,0,.3)
|
|
gc_rectangle('fill',x,y,w,h,4)
|
|
|
|
-- Frame
|
|
gc_setLineWidth(2)
|
|
gc_setColor(WIDGET.sel==self and COLOR.lN or COLOR.Z)
|
|
gc_rectangle('line',x,y,w,h,3)
|
|
|
|
-- Texts
|
|
FONT.set(self.font,self.fType)
|
|
gc_push('transform')
|
|
gc_translate(x,y)
|
|
|
|
-- Slider
|
|
gc_setColor(1,1,1)
|
|
if #list>self.capacity then
|
|
local len=h*h/H
|
|
gc_rectangle('fill',-15,(h-len)*scrollPos/(H-h),12,len,3)
|
|
end
|
|
|
|
-- Clear button
|
|
if not self.fix then
|
|
gc_rectangle('line',w-40,0,40,40,3)
|
|
gc_draw(self.sure==0 and clearIcon or sureIcon,w-40,0)
|
|
end
|
|
|
|
gc_setStencilTest('equal',1)
|
|
STW,STH=w,h
|
|
gc_stencil(_rectangleStencil)
|
|
gc_translate(0,-(scrollPos%lineH))
|
|
local pos=floor(scrollPos/lineH)
|
|
for i=pos+1,min(pos+self.capacity+1,#list) do
|
|
if list[i]~=nil then
|
|
gc_printf(list[i],10,4,w-16)
|
|
end
|
|
gc_translate(0,lineH)
|
|
end
|
|
gc_setStencilTest()
|
|
gc_pop()
|
|
end
|
|
function textBox:getInfo()
|
|
return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h)
|
|
end
|
|
function WIDGET.newTextBox(D)-- name,x,y,w,h[,font=30][,fType][,lineH][,fix],hide
|
|
local _={
|
|
name= D.name or "_",
|
|
|
|
resCtr={
|
|
D.x+D.w*.5,D.y+D.h*.5,
|
|
D.x+D.w*.5,D.y,
|
|
D.x-D.w*.5,D.y,
|
|
D.x,D.y+D.h*.5,
|
|
D.x,D.y-D.h*.5,
|
|
D.x,D.y,
|
|
D.x+D.w,D.y,
|
|
D.x,D.y+D.h,
|
|
D.x+D.w,D.y+D.h,
|
|
},
|
|
|
|
x= D.x,
|
|
y= D.y,
|
|
w= D.w,
|
|
h= D.h,
|
|
|
|
font= D.font or 30,
|
|
lineH=D.lineH,
|
|
capacity=nil,
|
|
fType=D.fType,
|
|
fix= D.fix,
|
|
texts={},
|
|
hideF=D.hideF,
|
|
hide= D.hide,
|
|
}
|
|
|
|
for k,v in next,textBox do _[k]=v end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
local listBox={
|
|
type='listBox',
|
|
keepFocus=true,
|
|
scrollPos=0,-- Scroll-down-distance
|
|
selected=0,-- Hidden wheel move value
|
|
_pressX=false,
|
|
_pressY=false,
|
|
}
|
|
function listBox:reset()
|
|
-- haha nothing here too, techmino is really fun!
|
|
end
|
|
function listBox:clear()
|
|
self.list={}
|
|
self.scrollPos=0
|
|
end
|
|
function listBox:setList(t)
|
|
assert(type(t)=='table',"Arg #1 must be table")
|
|
self.list=t
|
|
self.selected=1
|
|
self.scrollPos=0
|
|
end
|
|
function listBox:getList()
|
|
return self.list
|
|
end
|
|
function listBox:getLen()
|
|
return #self.list
|
|
end
|
|
function listBox:getSel()
|
|
return self.list[self.selected]
|
|
end
|
|
function listBox:isAbove(x,y)
|
|
return
|
|
x>self.x and
|
|
y>self.y and
|
|
x<self.x+self.w and
|
|
y<self.y+self.h
|
|
end
|
|
function listBox:getCenter()
|
|
return self.x+self.w*.5,self.y+self.h*.5
|
|
end
|
|
function listBox:push(t)
|
|
ins(self.list,t)
|
|
end
|
|
function listBox:pop()
|
|
if #self.list>0 then
|
|
rem(self.list)
|
|
listBox:drag(0,0,0,0)
|
|
end
|
|
end
|
|
function listBox:remove()
|
|
if self.selected then
|
|
rem(self.list,self.selected)
|
|
if not self.list[self.selected] then
|
|
self:arrowKey('up')
|
|
end
|
|
self:drag(0,0,0,0)
|
|
end
|
|
end
|
|
function listBox:press(x,y)
|
|
self._pressX=x
|
|
self._pressY=y
|
|
end
|
|
function listBox:release(x,y)
|
|
if not (x and y) then return end
|
|
if self._pressX then
|
|
self._pressX,self._pressY=false,false
|
|
x,y=x-self.x,y-self.y
|
|
if not (x and y and x>0 and y>0 and x<=self.w and y<=self.h) then return end
|
|
y=math.floor((y+self.scrollPos)/self.lineH)+1
|
|
if self.list[y] then
|
|
if self.selected~=y then
|
|
self.selected=y
|
|
self:drag(0,0,0,0)
|
|
SFX.play('selector',.8,0,12)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function listBox:drag(x,y,_,dy)
|
|
if x and y and self._pressX and MATH.distance(x,y,self._pressX,self._pressY)>10 then
|
|
self._pressX,self._pressY=false,false
|
|
end
|
|
self.scrollPos=max(0,min(self.scrollPos-dy,#self.list*self.lineH-self.h))
|
|
end
|
|
function listBox:scroll(n)
|
|
self:drag(nil,nil,nil,-n*self.lineH)
|
|
end
|
|
function listBox:arrowKey(dir)
|
|
if dir=="up" then
|
|
self.selected=max(self.selected-1,1)
|
|
if self.selected<floor(self.scrollPos/self.lineH)+2 then
|
|
self:drag(nil,nil,nil,self.lineH)
|
|
end
|
|
elseif dir=="down" then
|
|
self.selected=min(self.selected+1,#self.list)
|
|
if self.selected>floor(self.scrollPos/self.lineH)+self.capacity-1 then
|
|
self:drag(nil,nil,nil,-self.lineH)
|
|
end
|
|
end
|
|
end
|
|
function listBox:select(i)
|
|
self.selected=i
|
|
if self.selected<floor(self.scrollPos/self.lineH)+2 then
|
|
self:drag(nil,nil,nil,1e99)
|
|
elseif self.selected>floor(self.scrollPos/self.lineH)+self.capacity-1 then
|
|
self:drag(nil,nil,nil,-1e99)
|
|
end
|
|
end
|
|
function listBox:draw()
|
|
local x,y,w,h=self.x,self.y,self.w,self.h
|
|
local list=self.list
|
|
local scrollPos=self.scrollPos
|
|
local lineH=self.lineH
|
|
local H=#list*lineH
|
|
|
|
gc_push('transform')
|
|
gc_translate(x,y)
|
|
|
|
-- Background
|
|
gc_setColor(0,0,0,.4)
|
|
gc_rectangle('fill',0,0,w,h,4)
|
|
|
|
-- Frame
|
|
gc_setColor(WIDGET.sel==self and COLOR.lN or COLOR.Z)
|
|
gc_setLineWidth(2)
|
|
gc_rectangle('line',0,0,w,h,3)
|
|
|
|
-- Slider
|
|
if #list>self.capacity then
|
|
gc_setColor(1,1,1)
|
|
local len=h*h/H
|
|
gc_rectangle('fill',-15,(h-len)*scrollPos/(H-h),12,len,3)
|
|
end
|
|
|
|
-- List
|
|
gc_setStencilTest('equal',1)
|
|
STW,STH=w,h
|
|
gc_stencil(_rectangleStencil)
|
|
local pos=floor(scrollPos/lineH)
|
|
gc_translate(0,-(scrollPos%lineH))
|
|
for i=pos+1,min(pos+self.capacity+1,#list) do
|
|
if list[i]~=nil then
|
|
self.drawF(list[i],i,i==self.selected)
|
|
end
|
|
gc_translate(0,lineH)
|
|
end
|
|
gc_setStencilTest()
|
|
gc_pop()
|
|
end
|
|
function listBox:getInfo()
|
|
return("x=%d,y=%d,w=%d,h=%d"):format(self.x+self.w*.5,self.y+self.h*.5,self.w,self.h)
|
|
end
|
|
function WIDGET.newListBox(D)-- name,x,y,w,h,lineH,drawF[,hideF][,hide]
|
|
local _={
|
|
name= D.name or "_",
|
|
|
|
resCtr={
|
|
D.x+D.w*.5,D.y+D.h*.5,
|
|
D.x+D.w*.5,D.y,
|
|
D.x-D.w*.5,D.y,
|
|
D.x,D.y+D.h*.5,
|
|
D.x,D.y-D.h*.5,
|
|
D.x,D.y,
|
|
D.x+D.w,D.y,
|
|
D.x,D.y+D.h,
|
|
D.x+D.w,D.y+D.h,
|
|
},
|
|
|
|
x= D.x,
|
|
y= D.y,
|
|
w= D.w,
|
|
h= D.h,
|
|
|
|
list= {},
|
|
lineH= D.lineH,
|
|
capacity=ceil(D.h/D.lineH),
|
|
drawF= D.drawF,
|
|
hideF= D.hideF,
|
|
hide= D.hide,
|
|
}
|
|
|
|
for k,v in next,listBox do _[k]=v end
|
|
setmetatable(_,widgetMetatable)
|
|
return _
|
|
end
|
|
|
|
WIDGET.active={}-- Table contains all active widgets
|
|
WIDGET.scrollHeight=0-- Max drag height, not actual container height!
|
|
WIDGET.scrollPos=0-- Current scroll position
|
|
WIDGET.sel=false-- Selected widget
|
|
WIDGET.indexMeta={
|
|
__index=function(L,k)
|
|
for i=1,#L do
|
|
if L[i].name==k then
|
|
return L[i]
|
|
end
|
|
end
|
|
end
|
|
}
|
|
function WIDGET.setWidgetList(list)
|
|
WIDGET.unFocus(true)
|
|
WIDGET.active=list or NONE
|
|
WIDGET.cursorMove(SCR.xOy:inverseTransformPoint(love.mouse.getPosition()))
|
|
|
|
-- Reset all widgets
|
|
if list then
|
|
for i=1,#list do
|
|
list[i]:reset()
|
|
end
|
|
onChange()
|
|
end
|
|
end
|
|
function WIDGET.setScrollHeight(height)
|
|
WIDGET.scrollHeight=height and height or 0
|
|
WIDGET.scrollPos=0
|
|
end
|
|
function WIDGET.setLang(widgetText)
|
|
for S,L in next,SCN.scenes do
|
|
if L.widgetList then
|
|
for _,W in next,L.widgetList do
|
|
local t=W.fText or widgetText[S][W.name]
|
|
if not t and W.mustHaveText then
|
|
t=W.name or "##"
|
|
W.color=COLOR.dV
|
|
end
|
|
if type(W.setObject)=='function' then
|
|
W:setObject(t)
|
|
elseif type(t)=='string' and W.font then
|
|
W.obj=gc.newText(FONT.get(W.font or 30),t)
|
|
else
|
|
W.obj=t
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function WIDGET.getSelected()
|
|
return WIDGET.sel
|
|
end
|
|
function WIDGET.isFocus(W)
|
|
if W then
|
|
return W and WIDGET.sel==W
|
|
else
|
|
return WIDGET.sel~=false
|
|
end
|
|
end
|
|
function WIDGET.focus(W)
|
|
if WIDGET.sel==W or (W and W.hide) then return end
|
|
if WIDGET.sel and WIDGET.sel.type=='inputBox' then
|
|
kb.setTextInput(false)
|
|
EDITING=""
|
|
end
|
|
WIDGET.sel=W
|
|
if W and W.type=='inputBox' then
|
|
local _,y1=xOy:transformPoint(0,W.y+W.h)
|
|
kb.setTextInput(true,0,y1,1,1)
|
|
end
|
|
end
|
|
function WIDGET.unFocus(force)
|
|
local W=WIDGET.sel
|
|
if W and (force or not W.keepFocus) then
|
|
if W.type=='inputBox' then
|
|
kb.setTextInput(false)
|
|
EDITING=""
|
|
end
|
|
WIDGET.sel=false
|
|
end
|
|
end
|
|
|
|
function WIDGET.cursorMove(x,y)
|
|
for _,W in next,WIDGET.active do
|
|
if not W.hide and W.resCtr and W:isAbove(x,y+WIDGET.scrollPos) then
|
|
WIDGET.focus(W)
|
|
return
|
|
end
|
|
end
|
|
if WIDGET.sel and not WIDGET.sel.keepFocus then
|
|
WIDGET.unFocus()
|
|
end
|
|
end
|
|
function WIDGET.press(x,y,k)
|
|
local W=WIDGET.sel
|
|
if W then
|
|
if W.press then
|
|
W:press(x,y and y+WIDGET.scrollPos,k)
|
|
end
|
|
if W.hide then WIDGET.unFocus() end
|
|
end
|
|
end
|
|
function WIDGET.drag(x,y,dx,dy)
|
|
if WIDGET.sel then
|
|
local W=WIDGET.sel
|
|
if W.drag then
|
|
W:drag(x,y+WIDGET.scrollPos,dx,dy)
|
|
end
|
|
else
|
|
WIDGET.scrollPos=max(min(WIDGET.scrollPos-dy,WIDGET.scrollHeight),0)
|
|
end
|
|
end
|
|
function WIDGET.release(x,y,k)
|
|
local W=WIDGET.sel
|
|
if W and W.release then
|
|
W:release(x,y+WIDGET.scrollPos,k)
|
|
end
|
|
end
|
|
function WIDGET.textinput(texts)
|
|
local W=WIDGET.sel
|
|
if W and W.type=='inputBox' then
|
|
if (not W.regex or texts:match(W.regex)) and (not W.limit or #(WIDGET.sel.value..texts)<=W.limit) then
|
|
WIDGET.sel.value=WIDGET.sel.value..texts
|
|
SFX.play('touch')
|
|
else
|
|
SFX.play('drop_cancel')
|
|
end
|
|
end
|
|
end
|
|
|
|
function WIDGET.update(dt)
|
|
for _,W in next,WIDGET.active do
|
|
if W.hideF then
|
|
W.hide=W.hideF()
|
|
if W.hide and W==WIDGET.sel then
|
|
WIDGET.unFocus(true)
|
|
end
|
|
end
|
|
if W.update then W:update(dt) end
|
|
end
|
|
end
|
|
local widgetCanvas
|
|
local widgetCover do
|
|
local L={1,360,{'fRect',0,30,1,300}}
|
|
for i=0,30 do
|
|
ins(L,{'setCL',1,1,1,i/30})
|
|
ins(L,{'fRect',0,i,1,2})
|
|
ins(L,{'fRect',0,360-i,1,2})
|
|
end
|
|
widgetCover=GC.DO(L)
|
|
end
|
|
local scr_w,scr_h
|
|
function WIDGET.resize(w,h)
|
|
scr_w,scr_h=w,h
|
|
widgetCanvas=gc.newCanvas(w,h)
|
|
end
|
|
function WIDGET.draw()
|
|
gc_setCanvas({stencil=true},widgetCanvas)
|
|
gc_translate(0,-WIDGET.scrollPos)
|
|
for _,W in next,WIDGET.active do
|
|
if not W.hide then W:draw() end
|
|
end
|
|
gc_origin()
|
|
gc_setColor(1,1,1)
|
|
if WIDGET.scrollHeight>0 then
|
|
if WIDGET.scrollPos>0 then
|
|
gc_draw(upArrowIcon,scr_w*.5,10,0,SCR.k,nil,upArrowIcon:getWidth()*.5,0)
|
|
end
|
|
if WIDGET.scrollPos<WIDGET.scrollHeight then
|
|
gc_draw(downArrowIcon,scr_w*.5,scr_h-10,0,SCR.k,nil,downArrowIcon:getWidth()*.5,downArrowIcon:getHeight())
|
|
end
|
|
gc_setBlendMode('multiply','premultiplied')
|
|
gc_draw(widgetCover,nil,nil,nil,scr_w,scr_h/360)
|
|
end
|
|
gc_setCanvas({stencil=false})
|
|
gc_setBlendMode('alpha','premultiplied')
|
|
gc_draw(widgetCanvas)
|
|
gc_setBlendMode('alpha')
|
|
gc_replaceTransform(SCR.xOy)
|
|
end
|
|
|
|
return WIDGET
|