CharlyZM

Untitled

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