AlexCatze2005

forms.lua

Feb 27th, 2021
1,032
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
RAW Paste Data

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×