pepejik

Untitled

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