Advertisement
AlexCatze2005

forms.lua

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