pepejik

Untitled

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