pepejik

Untitled

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