pepejik

Untitled

Jan 2nd, 2025
16
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.43 KB | None | 0 0
  1. --------------------------------------------------
  2. -- GUI.lua
  3. -- Базовая библиотека GUI для OpenComputers
  4. -- (упрощённая версия в одном файле)
  5. --------------------------------------------------
  6. local component = require("component")
  7. local computer = require("computer")
  8. local event = require("event")
  9. local unicode = require("unicode")
  10. local gpu = component.gpu
  11.  
  12. --------------------------------------------------
  13. -- Объект GUI
  14. --------------------------------------------------
  15. local GUI = {}
  16. GUI.__index = GUI
  17.  
  18. -- Это "корневой" объект, хранящий сцены / окна.
  19. -- Но можно хранить в нём всё, что угодно.
  20. function GUI.new()
  21. local self = setmetatable({}, GUI)
  22. return self
  23. end
  24.  
  25. --------------------------------------------------
  26. -- Scene: для управления окнами, общим циклом событий
  27. --------------------------------------------------
  28. GUI.Scene = {}
  29. GUI.Scene.__index = GUI.Scene
  30.  
  31. function GUI.Scene:new()
  32. local scene = {
  33. windows = {},
  34. running = false,
  35. backgroundColor = 0x000000, -- фоновый цвет сцены
  36. }
  37. setmetatable(scene, GUI.Scene)
  38. return scene
  39. end
  40.  
  41. function GUI.Scene:addWindow(window)
  42. table.insert(self.windows, window)
  43. window.scene = self
  44. return window
  45. end
  46.  
  47. function GUI.Scene:draw()
  48. -- Заливка всей сцены (опционально)
  49. gpu.setBackground(self.backgroundColor)
  50. local w, h = gpu.getResolution()
  51. gpu.fill(1, 1, w, h, " ")
  52.  
  53. -- Рисуем все окна
  54. for _, win in ipairs(self.windows) do
  55. if win.draw then
  56. win:draw()
  57. end
  58. end
  59. end
  60.  
  61. -- Запуск основного цикла событий
  62. function GUI.Scene:run()
  63. self.running = true
  64. while self.running do
  65. local ev = { event.pull(0.05) }
  66. for _, win in ipairs(self.windows) do
  67. if win.handleEvent then
  68. win:handleEvent(ev)
  69. end
  70. end
  71.  
  72. -- (!) Перерисовываем всю сцену каждый раз
  73. gpu.clear()
  74. self:draw()
  75. end
  76. end
  77.  
  78. function GUI.Scene:stop()
  79. self.running = false
  80. end
  81.  
  82. --------------------------------------------------
  83. -- Window: Окно, содержащее элементы (виджеты)
  84. --------------------------------------------------
  85. GUI.Window = {}
  86. GUI.Window.__index = GUI.Window
  87.  
  88. function GUI.Window:new(x, y, width, height, title)
  89. local wnd = {
  90. x = x, y = y,
  91. width = width,
  92. height = height,
  93. title = title or "Window",
  94. background = 0x2D2D2D,
  95. foreground = 0xFFFFFF,
  96.  
  97. elements = {}, -- список элементов (Button, Label и т.п.)
  98. scene = nil, -- ссылается на Scene (устанавливается при addWindow)
  99. }
  100. setmetatable(wnd, GUI.Window)
  101. return wnd
  102. end
  103.  
  104. function GUI.Window:addElement(element)
  105. table.insert(self.elements, element)
  106. element.parent = self
  107. return element
  108. end
  109.  
  110. function GUI.Window:draw()
  111. -- Фон окна
  112. gpu.setBackground(self.background)
  113. gpu.setForeground(self.foreground)
  114. gpu.fill(self.x, self.y, self.width, self.height, " ")
  115.  
  116. -- Опционально: нарисовать заголовок
  117. local titlePosX = self.x + 1
  118. gpu.set(titlePosX, self.y, "[ " .. self.title .. " ]")
  119.  
  120. -- Рисуем элементы
  121. for _, elem in ipairs(self.elements) do
  122. if elem.draw then
  123. elem:draw()
  124. end
  125. end
  126. end
  127.  
  128. function GUI.Window:handleEvent(ev)
  129. -- Рассылаем событие элементам
  130. for _, elem in ipairs(self.elements) do
  131. if elem.handleEvent then
  132. elem:handleEvent(ev)
  133. end
  134. end
  135. end
  136.  
  137. --------------------------------------------------
  138. -- Button: кнопка
  139. --------------------------------------------------
  140. GUI.Button = {}
  141. GUI.Button.__index = GUI.Button
  142.  
  143. function GUI.Button:new(x, y, w, h, text, callback)
  144. local btn = {
  145. x = x, y = y,
  146. width = w, height = h,
  147. text = text or "Button",
  148. callback = callback, -- вызывается при клике
  149. background = 0x444444,
  150. foreground = 0xFFFFFF,
  151. disabled = false,
  152. }
  153. setmetatable(btn, GUI.Button)
  154. return btn
  155. end
  156.  
  157. function GUI.Button:draw()
  158. local px = self.parent.x + self.x - 1
  159. local py = self.parent.y + self.y - 1
  160.  
  161. gpu.setBackground(self.background)
  162. gpu.setForeground(self.foreground)
  163.  
  164. -- заливаем область кнопки
  165. gpu.fill(px, py, self.width, self.height, " ")
  166.  
  167. -- выводим текст (по центру строки)
  168. local textPosX = px + math.floor((self.width - unicode.len(self.text)) / 2)
  169. local textPosY = py + math.floor(self.height / 2)
  170. gpu.set(textPosX, textPosY, self.text)
  171. end
  172.  
  173. function GUI.Button:handleEvent(ev)
  174. if self.disabled then return end
  175. if ev[1] == "touch" then
  176. local _, _, tx, ty = table.unpack(ev)
  177. local px = self.parent.x + self.x - 1
  178. local py = self.parent.y + self.y - 1
  179.  
  180. if tx >= px and tx < px + self.width
  181. and ty >= py and ty < py + self.height then
  182. -- клик по кнопке
  183. if type(self.callback) == "function" then
  184. self.callback(self)
  185. end
  186. end
  187. end
  188. end
  189.  
  190. --------------------------------------------------
  191. -- RadioButton: радиокнопка
  192. -- Обычно нужно хранить "groupId", чтобы только одна
  193. -- кнопка в группе могла быть "включена".
  194. --------------------------------------------------
  195. GUI.RadioButton = {}
  196. GUI.RadioButton.__index = GUI.RadioButton
  197.  
  198. function GUI.RadioButton:new(x, y, label, groupId, checked, onChange)
  199. local rb = {
  200. x = x, y = y,
  201. label = label or "Radio",
  202. groupId = groupId or "defaultGroup",
  203. checked = (checked == true),
  204. onChange = onChange, -- callback, вызывается при переключении
  205. }
  206. setmetatable(rb, GUI.RadioButton)
  207. return rb
  208. end
  209.  
  210. function GUI.RadioButton:draw()
  211. local px = self.parent.x + self.x - 1
  212. local py = self.parent.y + self.y - 1
  213. gpu.setBackground(self.parent.background)
  214. gpu.setForeground(self.parent.foreground)
  215.  
  216. local marker = self.checked and "(*) " or "( ) "
  217. gpu.set(px, py, marker .. self.label)
  218. end
  219.  
  220. function GUI.RadioButton:handleEvent(ev)
  221. if ev[1] == "touch" then
  222. local _, _, tx, ty = table.unpack(ev)
  223. local px = self.parent.x + self.x - 1
  224. local py = self.parent.y + self.y - 1
  225. local strLen = unicode.len(self.label) + 4 -- "( ) " + label
  226.  
  227. if tx >= px and tx < px + strLen and ty == py then
  228. -- щёлкнули по радиокнопке => активируем
  229. self.checked = true
  230.  
  231. -- Нужно отключить (checked=false) у других RadioButton с той же группой
  232. -- (в рамках окна). Примерно так:
  233. for _, elem in ipairs(self.parent.elements) do
  234. if elem ~= self
  235. and elem.__index == GUI.RadioButton
  236. and elem.groupId == self.groupId then
  237. elem.checked = false
  238. end
  239. end
  240.  
  241. if type(self.onChange) == "function" then
  242. self.onChange(self)
  243. end
  244. end
  245. end
  246. end
  247.  
  248. --------------------------------------------------
  249. -- CheckBox: галочка
  250. --------------------------------------------------
  251. GUI.CheckBox = {}
  252. GUI.CheckBox.__index = GUI.CheckBox
  253.  
  254. function GUI.CheckBox:new(x, y, label, checked, onChange)
  255. local cb = {
  256. x = x, y = y,
  257. label = label or "Check me",
  258. checked = (checked == true),
  259. onChange = onChange,
  260. }
  261. setmetatable(cb, GUI.CheckBox)
  262. return cb
  263. end
  264.  
  265. function GUI.CheckBox:draw()
  266. local px = self.parent.x + self.x - 1
  267. local py = self.parent.y + self.y - 1
  268. gpu.setBackground(self.parent.background)
  269. gpu.setForeground(self.parent.foreground)
  270.  
  271. local marker = self.checked and "[X] " or "[ ] "
  272. gpu.set(px, py, marker .. self.label)
  273. end
  274.  
  275. function GUI.CheckBox:handleEvent(ev)
  276. if ev[1] == "touch" then
  277. local _, _, tx, ty = table.unpack(ev)
  278. local px = self.parent.x + self.x - 1
  279. local py = self.parent.y + self.y - 1
  280. local strLen = unicode.len(self.label) + 4 -- "[ ] " + label
  281.  
  282. if tx >= px and tx < px + strLen and ty == py then
  283. self.checked = not self.checked
  284. if type(self.onChange) == "function" then
  285. self.onChange(self, self.checked)
  286. end
  287. end
  288. end
  289. end
  290.  
  291. --------------------------------------------------
  292. -- Switch: переключатель (On/Off)
  293. --------------------------------------------------
  294. GUI.Switch = {}
  295. GUI.Switch.__index = GUI.Switch
  296.  
  297. function GUI.Switch:new(x, y, isOn, onToggle)
  298. local sw = {
  299. x = x, y = y,
  300. isOn = (isOn == true),
  301. onToggle = onToggle,
  302. }
  303. setmetatable(sw, GUI.Switch)
  304. return sw
  305. end
  306.  
  307. function GUI.Switch:draw()
  308. local px = self.parent.x + self.x - 1
  309. local py = self.parent.y + self.y - 1
  310. gpu.setBackground(self.parent.background)
  311. gpu.setForeground(self.parent.foreground)
  312.  
  313. local text = self.isOn and "[ON ]" or "[OFF]"
  314. gpu.set(px, py, text)
  315. end
  316.  
  317. function GUI.Switch:handleEvent(ev)
  318. if ev[1] == "touch" then
  319. local _, _, tx, ty = table.unpack(ev)
  320. local px = self.parent.x + self.x - 1
  321. local py = self.parent.y + self.y - 1
  322. local strLen = 5 -- "[ON ]" или "[OFF]" (5 символов)
  323.  
  324. if tx >= px and tx < px + strLen and ty == py then
  325. self.isOn = not self.isOn
  326. if type(self.onToggle) == "function" then
  327. self.onToggle(self, self.isOn)
  328. end
  329. end
  330. end
  331. end
  332.  
  333. --------------------------------------------------
  334. -- Label: простой текст
  335. --------------------------------------------------
  336. GUI.Label = {}
  337. GUI.Label.__index = GUI.Label
  338.  
  339. function GUI.Label:new(x, y, text)
  340. local lbl = {
  341. x = x, y = y,
  342. text = text or "Label",
  343. }
  344. setmetatable(lbl, GUI.Label)
  345. return lbl
  346. end
  347.  
  348. function GUI.Label:draw()
  349. local px = self.parent.x + self.x - 1
  350. local py = self.parent.y + self.y - 1
  351. gpu.setBackground(self.parent.background)
  352. gpu.setForeground(self.parent.foreground)
  353.  
  354. gpu.set(px, py, self.text)
  355. end
  356.  
  357. function GUI.Label:handleEvent(ev)
  358. -- По умолчанию Label только отображает текст,
  359. -- можно добавить логику, если нужно реагировать на клик.
  360. end
  361.  
  362. --------------------------------------------------
  363. -- TextEdit: поле ввода текста
  364. --------------------------------------------------
  365. GUI.TextEdit = {}
  366. GUI.TextEdit.__index = GUI.TextEdit
  367.  
  368. function GUI.TextEdit:new(x, y, width, text, onEnter)
  369. local te = {
  370. x = x, y = y,
  371. width = width,
  372. text = text or "",
  373. cursorPos = #text + 1,
  374. onEnter = onEnter, -- callback при нажатии Enter
  375. focus = false,
  376. }
  377. setmetatable(te, GUI.TextEdit)
  378. return te
  379. end
  380.  
  381. function GUI.TextEdit:draw()
  382. local px = self.parent.x + self.x - 1
  383. local py = self.parent.y + self.y - 1
  384.  
  385. gpu.setBackground(self.parent.background)
  386. gpu.setForeground(self.parent.foreground)
  387.  
  388. local shownText = self.text
  389. if unicode.len(shownText) > self.width - 1 then
  390. -- если текст длиннее, чем ширина поля, обрежем
  391. shownText = unicode.sub(shownText, - (self.width - 1))
  392. end
  393.  
  394. -- заливаем фон
  395. gpu.fill(px, py, self.width, 1, " ")
  396. -- выводим текст
  397. gpu.set(px, py, shownText)
  398.  
  399. -- если фокус есть, покажем курсор
  400. if self.focus then
  401. local cursorX = px + unicode.len(shownText)
  402. if cursorX < px + self.width then
  403. gpu.set(cursorX, py, "_")
  404. end
  405. end
  406. end
  407.  
  408. function GUI.TextEdit:handleEvent(ev)
  409. if ev[1] == "touch" then
  410. local _, _, tx, ty = table.unpack(ev)
  411. local px = self.parent.x + self.x - 1
  412. local py = self.parent.y + self.y - 1
  413.  
  414. if ty == py and tx >= px and tx < px + self.width then
  415. self.focus = true
  416. else
  417. self.focus = false
  418. end
  419. elseif ev[1] == "key_down" and self.focus then
  420. local _, char, code = table.unpack(ev)
  421. -- если это символ
  422. if char > 31 and char < 127 then
  423. -- добавляем символ
  424. self.text = self.text .. string.char(char)
  425. elseif code == 8 then
  426. -- backspace
  427. self.text = unicode.sub(self.text, 1, -2)
  428. elseif code == 13 then
  429. -- enter
  430. if self.onEnter then
  431. self.onEnter(self, self.text)
  432. end
  433. end
  434. end
  435. end
  436.  
  437. --------------------------------------------------
  438. -- Возвращаем объект GUI
  439. --------------------------------------------------
  440. return GUI
  441.  
Advertisement
Add Comment
Please, Sign In to add comment