ZNZNCOOP

forms

Jul 29th, 2015
2,870
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. local gpu=require("component").gpu
  2. local isPrimary=require("component").isPrimary
  3. local event=require("event")
  4. local len=require("unicode").len
  5. local sub=require("unicode").sub
  6. local uchar=require("unicode").char
  7. local term=require("term")
  8. local pushSignal=require("computer").pushSignal
  9. local kbd  = require("component").keyboard
  10. local wrap = require("text").wrap
  11. local padRight=require("text").padRight
  12. local isControl= require("keyboard").isControl
  13.  
  14. local forms={}
  15. local mouseEv={touch=true, scroll=true, drag=true, drop=true}
  16. local activeForm
  17.  
  18. local TComponent={left=1,top=1, color=0, fontColor=0xffffff, border=0, visible=true, tag=0, type=function() return "unknown" end}
  19. TComponent.__index=TComponent
  20.  
  21. function TComponent:paint() end
  22.  
  23. function TComponent:isVisible()
  24.   if not self.visible then return false end
  25.   if self.parent then return self.parent:isVisible()
  26.   else return self==activeForm
  27.   end
  28. end
  29.  
  30. function TComponent:draw()
  31.   if self.parent then self.X=self.parent.X+self.left-1 self.Y=self.parent.Y+self.top-1
  32.   else self.X=self.left self.Y=self.top end
  33.   gpu.setBackground(self.color)
  34.   gpu.setForeground(self.fontColor)
  35.   local brd=nil
  36.   if self.border==1 then brd={"┌","─","┐","└","│","┘"}
  37.   elseif self.border==2 then brd={"╔","═","╗","╚","║","╝"}
  38.   end
  39.   if brd then
  40.     gpu.set(self.X,self.Y, brd[1]..string.rep(brd[2],self.W-2)..brd[3])
  41.     for i=self.Y+1,self.Y+self.H-2 do
  42.       gpu.set(self.X,i, brd[5]..string.rep(" ",self.W-2)..brd[5])
  43.     end
  44.     gpu.set(self.X,self.Y+self.H-1, brd[4]..string.rep(brd[2],self.W-2)..brd[6])
  45.   else gpu.fill(self.X,self.Y,self.W,self.H," ") end
  46.   self:paint()
  47.   if self.elements then
  48.     for i=1,#self.elements do
  49.       if self.elements[i].visible then self.elements[i]:draw() end
  50.     end
  51.   end
  52. end
  53.  
  54. function TComponent:redraw() if self:isVisible() then self:draw() end end
  55.  
  56. function TComponent:makeChild(el)
  57.   if not self.elements then self.elements={} end
  58.   el.parent=self
  59.   table.insert(self.elements,el)
  60. end
  61.  
  62. function TComponent:mouseEv(ev,x,y,btn,user)
  63.   if self.elements then
  64.     for i=#self.elements,1,-1 do
  65.       local e=self.elements[i]
  66.       if e.visible and e.X and x>=e.X and x<e.X+e.W and y>=e.Y and y<e.Y+e.H then
  67.         e:mouseEv(ev,x,y,btn,user)
  68.         return
  69.       end
  70.     end
  71.   end  
  72.   if self[ev] then self[ev](self, x-self.X+1,y-self.Y+1,btn,user) end
  73. end
  74.  
  75. function TComponent:hide()
  76.   if self.parent then
  77.     self.visible=false
  78.     self.parent:draw()
  79.   else
  80.     gpu.setBackground(0)
  81.     gpu.fill(self.X,self.Y,self.W,self.H," ")
  82.   end
  83. end
  84.  
  85. function TComponent:show()
  86.   if self.parent then
  87.     self.visible=true
  88.     self.parent:draw()
  89.   else
  90.     self:draw()
  91.   end
  92. end
  93.  
  94. function TComponent:destruct()
  95.   if self.parent then
  96.     for i=1,#self.parent.elements do
  97.       if self.parent.elements[i]==self then table.remove(self.parent.elements,i) break end
  98.     end
  99.   end
  100. end
  101.  
  102. function forms.activeForm() return activeForm end
  103.  
  104. ----------Визуальные компоненты---------
  105. ------------------Form------------------
  106. local TForm=setmetatable({type=function() return "Form" end},TComponent)
  107. TForm.__index=TForm
  108.  
  109. function TForm:isActive() return self==activeForm end
  110.  
  111. function TForm:setActive()
  112.   if activeForm~=self then
  113.     activeForm=self
  114.     self:show()
  115.   end  
  116. end
  117.  
  118. function forms.addForm()
  119.   local obj={}
  120.   TForm.W, TForm.H=gpu.getResolution()
  121.   return setmetatable(obj,TForm)
  122. end
  123.  
  124. ------------------Button----------------
  125. local TButton=setmetatable({W=10, H=1, color=0x606060, type=function() return "Button" end},TComponent)
  126. TButton.__index=TButton
  127.  
  128. function TButton:touch(x, y, btn, user)
  129.   if self.onClick and btn==0 then self:onClick(user) end
  130. end
  131.  
  132. function TButton:paint()
  133.   gpu.set(self.X+(self.W-len(self.caption))/2,self.Y+(self.H-1)/2, self.caption)
  134. end
  135.  
  136. function TComponent:addButton(left, top, caption, onClick)
  137.   local obj={left=left, top=top, caption=caption or "Button", onClick=onClick}
  138.   self:makeChild(obj)
  139.   return setmetatable(obj,TButton)
  140. end
  141.  
  142. ------------------Label-----------------
  143. local TLabel=setmetatable({H=1, centered=false, alignRight=false, autoSize=true, type=function() return "Label" end} ,TComponent)
  144. TLabel.__index=TLabel
  145.  
  146. function TLabel:paint()
  147.  local line
  148.  local value=tostring(self.caption)
  149.  if self.autoSize then
  150.    self.W,self.H=0,0
  151.    for line in value:gmatch("([^\n]+)") do
  152.      self.H=self.H+1
  153.      if len(line)>self.W then self.W=len(line) end
  154.    end
  155.    if self.W<1 then self.W=1 end
  156.    if self.H<1 then self.H=1 end
  157.  end
  158.  for i=0,self.H-1 do
  159.   if not value then break end
  160.   line, value = wrap(value, self.W, self.W)
  161.   if self.centered then gpu.set(self.X+(self.W-len(line))/2,self.Y+i, line)
  162.   else
  163.     if self.alignRight then gpu.set(self.X+self.W-len(line),self.Y+i, line)
  164.     else gpu.set(self.X,self.Y+i, line) end
  165.   end
  166.  end
  167. end
  168.  
  169. function TComponent:addLabel(left, top, caption)
  170.   local obj={left=left, top=top, caption=caption or "Label"}
  171.   obj.W=len(obj.caption)
  172.   self:makeChild(obj)
  173.   return setmetatable(obj,TLabel)
  174. end
  175.  
  176. ------------------Edit------------------
  177. local TEdit=setmetatable({W=20, H=3, text="", border=1, type=function() return "Edit" end},TComponent)
  178. TEdit.__index=TEdit
  179.  
  180. function TEdit:paint()
  181.   if type(self.text)=="table" then
  182.     for i=1,self.H-2 do gpu.set(self.X+1,self.Y+i,sub(self.text[i] or "",1,self.W-2)) end
  183.   else gpu.set(self.X+1,self.Y+1, sub(self.text,1,self.W-2))
  184.   end
  185. end
  186.  
  187. local function editText(text,left,top,W,H)
  188. local running=true
  189. local scrollX, scrollY = 0, 0
  190. local posX, posY =1, 1
  191. local writeText
  192.  
  193. local function setCursor(nx,ny)
  194.   posX=nx or posX
  195.   posY=ny or posY
  196.   if #text<1 then text[1]="" end
  197.   if posY>#text then posY=#text end
  198.   if posY<1 then posY=1 end
  199.   if posX>len(text[posY])+1 then posX=len(text[posY])+1 end
  200.   if posX<1 then posX=1 end
  201.   local redraw=false
  202.   if posY<=scrollY then scrollY=posY-1 redraw=true end
  203.   if posY>scrollY+H then scrollY=posY-H redraw=true end
  204.   if posX<=scrollX then scrollX=posX-1 redraw=true end
  205.   if posX>scrollX+W then scrollX=posX-W redraw=true end
  206.   if redraw then writeText()
  207.   else term.setCursor(left+posX-scrollX-1, top+posY-scrollY-1) end
  208. end
  209.  
  210. local function writeLine(n)
  211.   gpu.set(left,top+n-scrollY-1,padRight(sub(text[n] or "",scrollX+1,scrollX+W),W))
  212. end
  213.  
  214. function writeText()
  215.   for i=1,H do writeLine(i+scrollY) end
  216.   setCursor()
  217. end
  218.  
  219. local function insert(value)
  220.   if not value or len(value) < 1 then return end
  221.   text[posY]=sub(text[posY],1,posX-1)..value..sub(text[posY],posX)
  222.   writeLine(posY)
  223.   setCursor(posX+len(value))
  224. end
  225.  
  226. local keys={}
  227. keys[203]=function()    -- Left
  228.   if posX>1 then setCursor(posX-1)
  229.   else if posY>1 then posY=posY-1 setCursor(len(text[posY])+1) end
  230.   end
  231. end
  232. keys[205]=function()   -- Right
  233.   if posX<=len(text[posY]) then setCursor(posX+1)
  234.   else if posY<#text then setCursor(1,posY+1) end
  235.   end
  236. end
  237. keys[199]=function() setCursor(1) end   -- Home
  238. keys[207]=function() setCursor(len(text[posY])+1) end   -- End
  239. keys[211]=function()    -- Del
  240.   if posX<=len(text[posY]) then
  241.     text[posY]=sub(text[posY],1,posX-1)..sub(text[posY],posX+1)
  242.     writeLine(posY)
  243.   else
  244.     if posY<#text then
  245.       text[posY]=text[posY]..text[posY+1]
  246.       table.remove(text,posY+1)
  247.       writeText()
  248.     end
  249.   end
  250. end
  251. keys[14] =function()    -- BackSp
  252.   if posX>1 then
  253.     text[posY]=sub(text[posY],1,posX-2)..sub(text[posY],posX)
  254.     writeLine(posY)
  255.     setCursor(posX-1)
  256.   else
  257.     if posY>1 then
  258.       posX,posY,text[posY-1]=len(text[posY-1])+1,posY-1,text[posY-1]..text[posY]
  259.       table.remove(text,posY+1)
  260.       writeText()
  261.     end
  262.   end
  263. end
  264. keys[15] =function() insert("  ") end   -- Tab
  265.  
  266. local function onKeyDown(char, code)
  267.   if keys[code] then keys[code]()
  268.   else if not isControl(char) then insert(uchar(char)) end
  269.   end
  270. end
  271.  
  272. local function onClipboard(value)
  273. end
  274.  
  275. local function onClick(x,y)
  276.   if x>=left and x<left+W and y>=top and y<top+H then
  277.     setCursor(x+scrollX-left+1,y+scrollY-top+1)
  278.   else running=false
  279.   end
  280. end
  281.  
  282. local function onScroll(direction)
  283. end
  284.  
  285. if type(text)=="table" then
  286.   keys[68] =function() running=false end   -- F10
  287.   keys[200]=function() setCursor(posX,posY-1) end   -- Up
  288.   keys[208]=function() setCursor(posX,posY+1) end   -- Down
  289.   keys[28] =function()   -- Enter
  290.     local n=len(text[posY]:match("^%s*"))
  291.     table.insert(text,posY+1,string.rep(" ",n)..sub(text[posY],posX))
  292.     text[posY]=sub(text[posY],1,posX-1)
  293.     posX,posY=n+1,posY+1
  294.     writeText()
  295.   end
  296. else
  297.   posX=len(text)+1
  298.   text={tostring(text)}
  299.   keys[28] =function() running=false end   -- Enter
  300. end
  301. term.setCursorBlink(true)
  302. writeText()
  303. local event, address, arg1, arg2, arg3
  304. while running do
  305.   event, address, arg1, arg2, arg3 = term.pull()
  306.   if type(address) == "string" and isPrimary(address) then
  307.     term.setCursorBlink(false)
  308.     if event == "key_down" then onKeyDown(arg1, arg2)
  309.     elseif event == "clipboard" then onClipboard(arg1)
  310.     elseif event == "touch" or event == "drag" then onClick(arg1, arg2)
  311.     elseif event == "scroll" then onScroll(arg3)
  312.     end
  313.     term.setCursorBlink(true)
  314.   end
  315. end
  316. if event=="touch" then pushSignal( event, address, arg1, arg2, arg3 ) end
  317. term.setCursorBlink(false)
  318. return text[1]
  319. end
  320.  
  321. function TEdit:touch(x, y, btn, user)
  322.   if btn==0 then
  323.     gpu.setBackground(self.color)
  324.     gpu.setForeground(self.fontColor)
  325.     if type(self.text)=="table" then editText(self.text,self.X+1,self.Y+1,self.W-2,self.H-2)
  326.     else self.text=editText(self.text,self.X+1,self.Y+1,self.W-2,1) end
  327.     self:draw()
  328.     if self.onEnter then self:onEnter(user) end
  329.   end
  330. end
  331.  
  332. function TComponent:addEdit(left, top, onEnter)
  333.   local obj={left=left, top=top, onEnter=onEnter}
  334.   self:makeChild(obj)
  335.   return setmetatable(obj,TEdit)
  336. end
  337.  
  338. ------------------Frame-----------------
  339. local TFrame=setmetatable({W=20, H=10, border=1, type=function() return "Frame" end},TComponent)
  340. TFrame.__index=TFrame
  341.  
  342. function TComponent:addFrame(left, top, border)
  343.   local obj={left=left, top=top, border=border}
  344.   self:makeChild(obj)
  345.   return setmetatable(obj,TFrame)
  346. end
  347.  
  348. ------------------List------------------
  349. local TList=setmetatable({W=20, H=10, border=2, selColor=0x0000ff, sfColor=0xffff00, shift=0, index=0,
  350.   type=function() return "List" end},TComponent)
  351. TList.__index=TList
  352.  
  353. function TList:paint()
  354.   local b= self.border==0 and 0 or 1
  355.   for i=1,self.H-2*b do
  356.     if i+self.shift==self.index then gpu.setForeground(self.sfColor) gpu.setBackground(self.selColor) end
  357.     gpu.set(self.X+b,self.Y+i+b-1, padRight(sub(self.lines[i+self.shift] or "",1,self.W-2*b),self.W-2*b))
  358.     if i+self.shift==self.index then gpu.setForeground(self.fontColor) gpu.setBackground(self.color) end
  359.   end
  360. end
  361.  
  362. function TList:clear()
  363.   self.shift=0 self.index=0 self.lines={} self.items={}
  364.   self:redraw()
  365. end
  366.  
  367. function TList:insert(pos,line,item)
  368.   if type(pos)~="number" then pos,line,item=#self.lines+1,pos,line end
  369.   table.insert(self.lines,pos,line)
  370.   table.insert(self.items,pos,item or false)
  371.   if self.index<1 then self.index=1 end
  372.   if pos<self.shift+self.H-1 then self:redraw() end
  373. end
  374.  
  375. function TList:sort(comp)
  376.   comp=comp or function(list,i,j) return list.lines[j]<list.lines[i] end
  377.   for i=1,#self.lines-1 do
  378.     for j=i+1,#self.lines do
  379.       if comp(self,i,j) then
  380.         if self.index==i then self.index=j
  381.         elseif self.index==j then self.index=i end
  382.         self.lines[i],self.lines[j]=self.lines[j],self.lines[i]
  383.         self.items[i],self.items[j]=self.items[j],self.items[i]
  384.       end
  385.     end
  386.   end
  387.   self:redraw()
  388. end
  389.  
  390. function TList:touch(x, y, btn, user)
  391.   local b= self.border==0 and 0 or 1
  392.   if x>b and x<=self.W-b and y>b and y<=self.H-b and btn==0 then
  393.     local i=self.shift+y-b
  394.     if self.index~=i and self.lines[i] then
  395.       self.index=i
  396.       self:redraw()
  397.       if self.onChange then self:onChange(self.lines[i],self.items[i],user) end
  398.     end
  399.   end
  400. end
  401.  
  402. function TList:scroll(x, y, sh, user)
  403.   local b= self.border==0 and 0 or 1
  404.   self.shift=self.shift-sh
  405.   if self.shift>#(self.lines)-self.H+2*b then self.shift=#(self.lines)-self.H+2*b end
  406.   if self.shift<0 then self.shift=0 end
  407.   self:redraw()
  408. end
  409.  
  410. function TComponent:addList(left, top, onChange)
  411.   local obj={left=left, top=top, lines={}, items={}, onChange=onChange}
  412.   self:makeChild(obj)
  413.   return setmetatable(obj,TList)
  414. end
  415.  
  416. local work
  417. ---------Невизуальные компоненты--------
  418. local TInvisible=setmetatable({W=10, H=3, border=2, draw=function() end},TComponent)
  419. TInvisible.__index=TInvisible
  420. ------------------Event-----------------
  421. local TEvent=setmetatable({type=function() return "Event" end},TInvisible)
  422. TEvent.__index=TEvent
  423.  
  424. function TEvent:run()
  425.   if self.onEvent then forms.listen(self.eventName, self.onEvent) end
  426. end
  427.  
  428. function TEvent:stop()
  429.   forms.ignore(self.eventName, self.onEvent)
  430. end
  431.  
  432. function TComponent:addEvent(eventName, onEvent)
  433.   local obj={eventName=eventName, onEvent=onEvent}
  434.   self:makeChild(obj)
  435.   setmetatable(obj,TEvent)
  436.   obj:run()
  437.   return obj
  438. end
  439.  
  440. ------------------Timer-----------------
  441. local TTimer=setmetatable({Enabled=true, type=function() return "Timer" end},TInvisible)
  442. TTimer.__index=TTimer
  443.  
  444. function TTimer:run()
  445.   self.Enabled=nil
  446.   if self.onTime then
  447.     self.timerId=event.timer(self.interval,
  448.     function ()
  449.       if self.Enabled and work then
  450.         self.onTime(self)
  451.       else
  452.         self:stop()
  453.       end
  454.     end,
  455.     math.huge
  456.     )
  457.   end
  458. end
  459. function TTimer:stop()
  460.   self.Enabled=false
  461.   event.cancel(self.timerId)
  462. end
  463.  
  464. function TComponent:addTimer(interval, onTime)
  465.   local obj={interval=interval, onTime=onTime}
  466.   self:makeChild(obj)
  467.   setmetatable(obj,TTimer)
  468.   obj:run()
  469.   return obj
  470. end
  471.  
  472. -----------Обработчик событий-----------
  473. local listeners={}
  474.  
  475. function forms.listen(name, callback)
  476.   checkArg(1, name, "string")
  477.   checkArg(2, callback, "function")
  478.   if listeners[name] then
  479.     for i = 1, #listeners[name] do
  480.       if listeners[name][i] == callback then
  481.         return false
  482.       end
  483.     end
  484.   else
  485.     listeners[name] = {}
  486.   end
  487.   table.insert(listeners[name], callback)
  488.   return true
  489. end
  490.  
  491. function forms.ignore(name, callback)
  492.   checkArg(1, name, "string")
  493.   checkArg(2, callback, "function")
  494.   if listeners[name] then
  495.     for i = 1, #listeners[name] do
  496.       if listeners[name][i] == callback then
  497.         table.remove(listeners[name], i)
  498.         if #listeners[name] == 0 then
  499.           listeners[name] = nil
  500.         end
  501.         return true
  502.       end
  503.     end
  504.   end
  505.   return false
  506. end
  507.  
  508. function forms.ignoreAll()
  509.   listeners={}
  510. end
  511. ----------------------------------------
  512.  
  513. function forms.run(form)
  514.   work=true
  515.   local Fc, Bc = gpu.getForeground(), gpu.getBackground()
  516.   activeForm=form
  517.   activeForm:draw()
  518.   while work do
  519.     local ev,adr,x,y,btn,user=event.pull()
  520.     if mouseEv[ev] and adr==gpu.getScreen() then activeForm:mouseEv(ev,x,y,btn,user) end
  521.     if listeners[ev] then
  522.       for i=1,#listeners[ev] do listeners[ev][i](ev,adr,x,y,btn,user) end
  523.     end
  524.     if listeners[""] then
  525.       for i=1,#listeners[""] do listeners[""][i](ev,adr,x,y,btn,user) end
  526.     end
  527.   end
  528.   gpu.setForeground(Fc)
  529.   gpu.setBackground(Bc)
  530.   forms.ignoreAll()
  531. end
  532.  
  533. function forms.stop()
  534.   work=false
  535. end
  536.  
  537. return forms
RAW Paste Data