· 6 years ago · Apr 01, 2020, 12:56 PM
1
2local advancedLua = require("advancedLua")
3local component = require("component")
4local term = require("term")
5local unicode = require("unicode")
6local event = require("event")
7local fs = require("filesystem")
8local shell = require("shell")
9local keyboard = require("keyboard")
10local computer = require("computer")
11local serialization = require("serialization")
12
13
14local gpu = component.gpu
15local ecs = {}
16
17----------------------------------------------------------------------------------------------------
18
19ecs.windowColors = {
20 background = 0xeeeeee,
21 usualText = 0x444444,
22 subText = 0x888888,
23 tab = 0xaaaaaa,
24 title = 0xffffff,
25 shadow = 0x444444,
26}
27
28ecs.colors = {
29 white = 0xffffff,
30 orange = 0xF2B233,
31 magenta = 0xE57FD8,
32 lightBlue = 0x99B2F2,
33 yellow = 0xDEDE6C,
34 lime = 0x7FCC19,
35 pink = 0xF2B2CC,
36 gray = 0x4C4C4C,
37 lightGray = 0x999999,
38 cyan = 0x4C99B2,
39 purple = 0xB266E5,
40 blue = 0x3366CC,
41 brown = 0x7F664C,
42 green = 0x57A64E,
43 red = 0xCC4C4C,
44 black = 0x000000,
45 ["0"] = 0xffffff,
46 ["1"] = 0xF2B233,
47 ["2"] = 0xE57FD8,
48 ["3"] = 0x99B2F2,
49 ["4"] = 0xDEDE6C,
50 ["5"] = 0x7FCC19,
51 ["6"] = 0xF2B2CC,
52 ["7"] = 0x4C4C4C,
53 ["8"] = 0x999999,
54 ["9"] = 0x4C99B2,
55 ["a"] = 0xB266E5,
56 ["b"] = 0x3366CC,
57 ["c"] = 0x7F664C,
58 ["d"] = 0x57A64E,
59 ["e"] = 0xCC4C4C,
60 ["f"] = 0x000000
61}
62
63----------------------------------------------------------------------------------------------------
64
65--Адекватный запрос к веб-серверу вместо стандартного Internet API, бросающего stderr, когда ему вздумается
66function ecs.internetRequest(url)
67 local success, response = pcall(component.internet.request, url)
68 if success then
69 local responseData = ""
70 while true do
71 local data, responseChunk = response.read()
72 if data then
73 responseData = responseData .. data
74 else
75 if responseChunk then
76 return false, responseChunk
77 else
78 return true, responseData
79 end
80 end
81 end
82 else
83 return false, reason
84 end
85end
86
87--Загрузка файла с инета
88function ecs.getFileFromUrl(url, path)
89 local success, response = ecs.internetRequest(url)
90 if success then
91 fs.makeDirectory(fs.path(path) or "")
92 local file = io.open(path, "w")
93 file:write(response)
94 file:close()
95 else
96 ecs.error("Could not connect to to URL address \"" .. url .. "\"")
97 return
98 end
99end
100
101--Отключение принудительного завершения программ
102function ecs.disableInterrupting()
103 _G.eventInterruptBackup = package.loaded.event.shouldInterrupt
104 _G.eventSoftInterruptBackup = package.loaded.event.shouldSoftInterrupt
105
106 package.loaded.event.shouldInterrupt = function () return false end
107 package.loaded.event.shouldSoftInterrupt = function () return false end
108end
109
110--Включение принудительного завершения программ
111function ecs.enableInterrupting()
112 if _G.eventInterruptBackup then
113 package.loaded.event.shouldInterrupt = _G.eventInterruptBackup
114 package.loaded.event.shouldSoftInterrupt = _G.eventSoftInterruptBackup
115 else
116 error("Cant't enable interrupting beacause of it's already enabled.")
117 end
118end
119
120function ecs.getScaledResolution(scale, debug)
121 --Базовая коррекция масштаба, чтобы всякие умники не писали своими погаными ручонками, чего не следует
122 if scale > 1 then
123 scale = 1
124 elseif scale < 0.1 then
125 scale = 0.1
126 end
127
128 --Просчет монитора в псевдопикселях - забей, даже объяснять не буду, работает как часы
129 local function calculateAspect(screens)
130 local abc = 12
131
132 if screens == 2 then
133 abc = 28
134 elseif screens > 2 then
135 abc = 28 + (screens - 2) * 16
136 end
137
138 return abc
139 end
140
141 --Рассчитываем пропорцию монитора в псевдопикселях
142 local xScreens, yScreens = component.proxy(component.gpu.getScreen()).getAspectRatio()
143 local xPixels, yPixels = calculateAspect(xScreens), calculateAspect(yScreens)
144 local proportion = xPixels / yPixels
145
146 --Получаем максимально возможное разрешение данной видеокарты
147 local xMax, yMax = component.gpu.maxResolution()
148
149 --Получаем теоретическое максимальное разрешение монитора с учетом его пропорции, но без учета лимита видеокарты
150 local newWidth, newHeight
151 if proportion >= 1 then
152 newWidth = xMax
153 newHeight = math.floor(newWidth / proportion / 2)
154 else
155 newHeight = yMax
156 newWidth = math.floor(newHeight * proportion * 2)
157 end
158
159 --Получаем оптимальное разрешение для данного монитора с поддержкой видеокарты
160 local optimalNewWidth, optimalNewHeight = newWidth, newHeight
161
162 if optimalNewWidth > xMax then
163 local difference = newWidth / xMax
164 optimalNewWidth = xMax
165 optimalNewHeight = math.ceil(newHeight / difference)
166 end
167
168 if optimalNewHeight > yMax then
169 local difference = newHeight / yMax
170 optimalNewHeight = yMax
171 optimalNewWidth = math.ceil(newWidth / difference)
172 end
173
174 --Корректируем идеальное разрешение по заданному масштабу
175 local finalNewWidth, finalNewHeight = math.floor(optimalNewWidth * scale), math.floor(optimalNewHeight * scale)
176
177 --Выводим инфу, если нужно
178 if debug then
179 print(" ")
180 print("Максимальное разрешение: "..xMax.."x"..yMax)
181 print("Пропорция монитора: "..xPixels.."x"..yPixels)
182 print("Коэффициент пропорции: "..proportion)
183 print(" ")
184 print("Теоретическое разрешение: "..newWidth.."x"..newHeight)
185 print("Оптимизированное разрешение: "..optimalNewWidth.."x"..optimalNewHeight)
186 print(" ")
187 print("Новое разрешение: "..finalNewWidth.."x"..finalNewHeight)
188 print(" ")
189 end
190
191 return finalNewWidth, finalNewHeight
192end
193
194--Установка масштаба монитора
195function ecs.setScale(scale, debug)
196 --Устанавливаем выбранное разрешение
197 component.gpu.setResolution(ecs.getScaledResolution(scale, debug))
198end
199
200function ecs.rebindGPU(address)
201 component.gpu.bind(address)
202end
203
204--Получаем всю инфу об оперативку в килобайтах
205function ecs.getInfoAboutRAM()
206 local free = math.floor(computer.freeMemory() / 1024)
207 local total = math.floor(computer.totalMemory() / 1024)
208 local used = total - free
209
210 return free, total, used
211end
212
213--Получить информацию о жестких дисках
214function ecs.getHDDs()
215 local candidates = {}
216 for address in component.list("filesystem") do
217 local proxy = component.proxy(address)
218 if proxy.address ~= computer.tmpAddress() and proxy.getLabel() ~= "internet" then
219 local isFloppy, spaceTotal = false, math.floor(proxy.spaceTotal() / 1024)
220 if spaceTotal < 600 then isFloppy = true end
221 table.insert(candidates, {
222 ["spaceTotal"] = spaceTotal,
223 ["spaceUsed"] = math.floor(proxy.spaceUsed() / 1024),
224 ["label"] = proxy.getLabel(),
225 ["address"] = proxy.address,
226 ["isReadOnly"] = proxy.isReadOnly(),
227 ["isFloppy"] = isFloppy,
228 })
229 end
230 end
231 return candidates
232end
233
234--Форматировать диск
235function ecs.formatHDD(address)
236 local proxy = component.proxy(address)
237 local list = proxy.list("")
238 ecs.info("auto", "auto", "", "Formatting disk...")
239 for _, file in pairs(list) do
240 if type(file) == "string" then
241 if not proxy.isReadOnly(file) then proxy.remove(file) end
242 end
243 end
244 list = nil
245end
246
247--Установить имя жесткого диска
248function ecs.setHDDLabel(address, label)
249 local proxy = component.proxy(address)
250 proxy.setLabel(label or "Untitled")
251end
252
253--Найти монтированный путь конкретного адреса диска
254function ecs.findMount(address)
255 for fs1, path in fs.mounts() do
256 if fs1.address == component.get(address) then
257 return path
258 end
259 end
260end
261
262function ecs.getArraySize(array)
263 local size = 0
264 for key in pairs(array) do
265 size = size + 1
266 end
267 return size
268end
269
270--Скопировать файлы с одного диска на другой с заменой
271function ecs.duplicateFileSystem(fromAddress, toAddress)
272 local source, destination = ecs.findMount(fromAddress), ecs.findMount(toAddress)
273 ecs.info("auto", "auto", "", "Copying file system...")
274 shell.execute("bin/cp -rx "..source.."* "..destination)
275end
276
277--Загрузка файла с пастебина
278function ecs.getFromPastebin(paste, path)
279 local url = "http://pastebin.com/raw.php?i=" .. paste
280 ecs.getFileFromUrl(url, path)
281end
282
283--Загрузка файла с гитхаба
284function ecs.getFromGitHub(url, path)
285 url = "https://raw.githubusercontent.com/" .. url
286 ecs.getFileFromUrl(url, path)
287end
288
289--Загрузить ОС-приложение
290function ecs.getOSApplication(application)
291 --Если это приложение
292 if application.type == "Application" then
293 --Удаляем приложение, если оно уже существовало и создаем все нужные папочки
294 application.name = "/" .. application.name
295 fs.remove(application.name .. ".app")
296 fs.makeDirectory(application.name .. ".app/Resources")
297
298 --Загружаем основной исполняемый файл и иконку
299 ecs.getFromGitHub(application.url, application.name .. ".app/Main.lua")
300 ecs.getFromGitHub(application.icon, application.name .. ".app/Resources/Icon.pic")
301
302 --Если есть ресурсы, то загружаем ресурсы
303 if application.resources then
304 for i = 1, #application.resources do
305 ecs.getFromGitHub(application.resources[i].url, application.name .. ".app/Resources/" .. application.resources[i].name)
306 end
307 end
308
309 --Если есть файл "о программе", то грузим и его
310 if application.about then
311 ecs.getFromGitHub(application.about .. _G.OSSettings.language .. ".txt", application.name .. ".app/Resources/About/" .. _G.OSSettings.language .. ".txt")
312 end
313
314 --Если имеется режим создания ярлыка, то создаем его
315 if application.createShortcut then
316 local desktopPath = "MineOS/Desktop/"
317
318 if application.createShortcut == "desktop" then
319 ecs.createShortCut(desktopPath .. fs.name(application.name) .. ".lnk", application.name .. ".app")
320 end
321 end
322
323 --Если тип = другой, чужой, а мб и свой пастебин
324 elseif application.type == "Pastebin" then
325 ecs.getFromPastebin(application.url, application.name)
326
327 --Если просто какой-то скрипт
328 elseif application.type == "Script" or application.type == "Library" or application.type == "Icon" or application.type == "Wallpaper" then
329 ecs.getFromGitHub(application.url, application.name)
330
331 --А если ваще какая-то абстрактная хуйня, либо ссылка на веб, то загружаем по УРЛ-ке
332 else
333 ecs.getFileFromUrl(application.url, application.name)
334 end
335end
336
337--Получить список приложений, которые требуется обновить
338function ecs.getAppsToUpdate(debug)
339 --Задаем стартовые пути
340 local pathToApplicationsFile = "MineOS/System/OS/Applications.txt"
341 local pathToSecondApplicationsFile = "MineOS/System/OS/Applications2.txt"
342 --Путь к файл-листу на пастебине
343 local paste = "3j2x4dDn"
344 --Выводим инфу
345 local oldPixels
346 if debug then oldPixels = ecs.info("auto", "auto", " ", "Checking for updates...") end
347 --Получаем свеженький файл
348 ecs.getFromPastebin(paste, pathToSecondApplicationsFile)
349 --Читаем оба файла
350 local file = io.open(pathToApplicationsFile, "r")
351 local applications = serialization.unserialize(file:read("*a"))
352 file:close()
353 --И второй
354 file = io.open(pathToSecondApplicationsFile, "r")
355 local applications2 = serialization.unserialize(file:read("*a"))
356 file:close()
357
358 local countOfUpdates = 0
359
360 --Просматриваем свеженький файлик и анализируем, че в нем нового, все старое удаляем
361 local i = 1
362 while true do
363 --Разрыв цикла
364 if i > #applications2 then break end
365 --Новая версия файла
366 local newVersion, oldVersion = applications2[i].version, 0
367 --Получаем старую версию этого файла
368 for j = 1, #applications do
369 if applications2[i].name == applications[j].name then
370 oldVersion = applications[j].version or 0
371 break
372 end
373 end
374 --Если новая версия новее, чем старая, то добавить в массив то, что нужно обновить
375 if newVersion > oldVersion then
376 applications2[i].needToUpdate = true
377 countOfUpdates = countOfUpdates + 1
378 end
379
380 i = i + 1
381 end
382 --Если чет рисовалось, то стереть на хер
383 if oldPixels then ecs.drawOldPixels(oldPixels) end
384 --Возвращаем массив с тем, че нужно обновить и просто старый аппликашнс на всякий случай
385 return applications2, countOfUpdates
386end
387
388--Сделать строку пригодной для отображения в ОпенКомпах
389--Заменяет табсы на пробелы и виндовый возврат каретки на человеческий UNIX-овский
390function ecs.stringOptimize(sto4ka, indentatonWidth)
391 sto4ka = string.gsub(sto4ka, "\r\n", "\n")
392 sto4ka = string.gsub(sto4ka, " ", string.rep(" ", indentatonWidth or 2))
393 return stro4ka
394end
395
396--ИЗ ДЕСЯТИЧНОЙ В ШЕСТНАДЦАТИРИЧНУЮ
397function ecs.decToBase(IN,BASE)
398 local hexCode = "0123456789ABCDEFGHIJKLMNOPQRSTUVW"
399 OUT = ""
400 local ostatok = 0
401 while IN>0 do
402 ostatok = math.fmod(IN,BASE) + 1
403 IN = math.floor(IN/BASE)
404 OUT = string.sub(hexCode,ostatok,ostatok)..OUT
405 end
406 if #OUT == 1 then OUT = "0"..OUT end
407 if OUT == "" then OUT = "00" end
408 return OUT
409end
410
411--Правильное конвертирование HEX-переменной в строковую
412function ecs.HEXtoString(color, bitCount, withNull)
413 local stro4ka = string.format("%X",color)
414 local sStro4ka = unicode.len(stro4ka)
415 if sStro4ka < bitCount then
416 stro4ka = string.rep("0", bitCount - sStro4ka) .. stro4ka
417 end
418 sStro4ka = nil
419 if withNull then return "0x"..stro4ka else return stro4ka end
420end
421
422--КЛИКНУЛИ ЛИ В ЗОНУ
423function ecs.clickedAtArea(x,y,sx,sy,ex,ey)
424 if (x >= sx) and (x <= ex) and (y >= sy) and (y <= ey) then return true end
425 return false
426end
427
428--Заливка всего экрана указанным цветом
429function ecs.clearScreen(color)
430 if color then component.gpu.setBackground(color) end
431 term.clear()
432end
433
434--Установка пикселя нужного цвета
435function ecs.setPixel(x,y,color)
436 component.gpu.setBackground(color)
437 component.gpu.set(x,y," ")
438end
439
440--Простая установка цветов в одну строку, ибо я ленивый
441function ecs.setColor(background, foreground)
442 component.gpu.setBackground(background)
443 component.gpu.setForeground(foreground)
444end
445
446--Цветной текст
447function ecs.colorText(x,y,textColor,text)
448 component.gpu.setForeground(textColor)
449 component.gpu.set(x,y,text)
450end
451
452--Цветной текст с жопкой!
453function ecs.colorTextWithBack(x,y,textColor,backColor,text)
454 component.gpu.setForeground(textColor)
455 component.gpu.setBackground(backColor)
456 component.gpu.set(x,y,text)
457end
458
459--Инверсия цвета
460function ecs.invertColor(color)
461 return 0xffffff - color
462end
463
464--Адаптивный текст, подстраивающийся под фон
465function ecs.adaptiveText(x,y,text,textColor)
466 component.gpu.setForeground(textColor)
467 x = x - 1
468 for i=1,unicode.len(text) do
469 local info = {component.gpu.get(x+i,y)}
470 component.gpu.setBackground(info[3])
471 component.gpu.set(x+i,y,unicode.sub(text,i,i))
472 end
473end
474
475--Костыльная замена обычному string.find()
476--Работает медленнее, но хотя бы поддерживает юникод
477function unicode.find(str, pattern, init, plain)
478 if init then
479 if init < 0 then
480 init = -#unicode.sub(str,init)
481 elseif init > 0 then
482 init = #unicode.sub(str,1,init-1)+1
483 end
484 end
485
486 a, b = string.find(str, pattern, init, plain)
487
488 if a then
489 local ap,bp = str:sub(1,a-1), str:sub(a,b)
490 a = unicode.len(ap)+1
491 b = a + unicode.len(bp)-1
492 return a,b
493 else
494 return a
495 end
496end
497
498--Умный текст по аналогии с майнчатовским. Ставишь символ параграфа, указываешь хуйню - и хуякс! Работает!
499function ecs.smartText(x, y, text)
500 local sText = unicode.len(text)
501 local specialSymbol = "§"
502 --Разбираем по кусочкам строку и получаем цвета
503 local massiv = {}
504 local iterator = 1
505 local currentColor = component.gpu.getForeground()
506 while iterator <= sText do
507 local symbol = unicode.sub(text, iterator, iterator)
508 if symbol == specialSymbol then
509 currentColor = ecs.colors[unicode.sub(text, iterator + 1, iterator + 1) or "f"]
510 iterator = iterator + 1
511 else
512 table.insert(massiv, {symbol, currentColor})
513 end
514 symbol = nil
515 iterator = iterator + 1
516 end
517 x = x - 1
518 for i = 1, #massiv do
519 if currentColor ~= massiv[i][2] then currentColor = massiv[i][2]; component.gpu.setForeground(massiv[i][2]) end
520 component.gpu.set(x + i, y, massiv[i][1])
521 end
522end
523
524--Аналог умного текста, но использующий HEX-цвета для кодировки
525function ecs.formattedText(x, y, text, limit)
526 --Ограничение длины строки
527 limit = limit or math.huge
528 --Стартовая позиция курсора для отрисовки
529 local xPos = x
530 --Создаем массив символов данной строки
531 local symbols = {}
532 for i = 1, unicode.len(text) do table.insert(symbols, unicode.sub(text, i, i)) end
533 --Перебираем все символы строки, пока не переберем все или не достигнем указанного лимита
534 local i = 1
535 while i <= #symbols and i <= limit do
536 --Если находим символ параграфа, то
537 if symbols[i] == "§" then
538 --Меняем цвет текста на указанный
539 component.gpu.setForeground(tonumber("0x" .. symbols[i+1] .. symbols[i+2] .. symbols[i+3] .. symbols[i+4] .. symbols[i+5] .. symbols[i+6]))
540 --Увеличиваем лимит на 7, т.к.
541 limit = limit + 7
542 --Сдвигаем итератор цикла на 7
543 i = i + 7
544 end
545 --Рисуем символ на нужной позиции
546 component.gpu.set(xPos, y, symbols[i])
547 --Увеличиваем позицию курсора и итератор на 1
548 xPos = xPos + 1
549 i = i + 1
550 end
551end
552
553--Инвертированный текст на основе цвета фона
554function ecs.invertedText(x,y,symbol)
555 local info = {component.gpu.get(x,y)}
556 ecs.adaptiveText(x,y,symbol,ecs.invertColor(info[3]))
557end
558
559--Адаптивное округление числа
560function ecs.adaptiveRound(chislo)
561 local celaya,drobnaya = math.modf(chislo)
562 if drobnaya >= 0.5 then
563 return (celaya + 1)
564 else
565 return celaya
566 end
567end
568
569--Округление до опред. кол-ва знаков после запятой
570function ecs.round(num, idp)
571 local mult = 10^(idp or 0)
572 return math.floor(num * mult + 0.5) / mult
573end
574
575--Обычный квадрат указанного цвета
576function ecs.square(x,y,width,height,color)
577 component.gpu.setBackground(color)
578 component.gpu.fill(x,y,width,height," ")
579end
580
581--Юникодовская рамка
582function ecs.border(x, y, width, height, back, fore)
583 local stringUp = "┌"..string.rep("─", width - 2).."┐"
584 local stringDown = "└"..string.rep("─", width - 2).."┘"
585 component.gpu.setForeground(fore)
586 component.gpu.setBackground(back)
587 component.gpu.set(x, y, stringUp)
588 component.gpu.set(x, y + height - 1, stringDown)
589
590 local yPos = 1
591 for i = 1, (height - 2) do
592 component.gpu.set(x, y + yPos, "│")
593 component.gpu.set(x + width - 1, y + yPos, "│")
594 yPos = yPos + 1
595 end
596end
597
598--Кнопка в виде текста в рамке
599function ecs.drawFramedButton(x, y, width, height, text, color)
600 ecs.border(x, y, width, height, component.gpu.getBackground(), color)
601 component.gpu.fill(x + 1, y + 1, width - 2, height - 2, " ")
602 x = x + math.floor(width / 2 - unicode.len(text) / 2)
603 y = y + math.floor(width / 2 - 1)
604 component.gpu.set(x, y, text)
605end
606
607--Юникодовский разделитель
608function ecs.separator(x, y, width, back, fore)
609 ecs.colorTextWithBack(x, y, fore, back, string.rep("─", width))
610end
611
612--Автоматическое центрирование текста по указанной координате (x, y, xy)
613function ecs.centerText(mode,coord,text)
614 local dlina = unicode.len(text)
615 local xSize,ySize = component.gpu.getResolution()
616
617 if mode == "x" then
618 component.gpu.set(math.floor(xSize/2-dlina/2),coord,text)
619 elseif mode == "y" then
620 component.gpu.set(coord,math.floor(ySize/2),text)
621 else
622 component.gpu.set(math.floor(xSize/2-dlina/2),math.floor(ySize/2),text)
623 end
624end
625
626--Отрисовка "изображения" по указанному массиву
627function ecs.drawCustomImage(x,y,pixels)
628 x = x - 1
629 y = y - 1
630 local pixelsWidth = #pixels[1]
631 local pixelsHeight = #pixels
632 local xEnd = x + pixelsWidth
633 local yEnd = y + pixelsHeight
634
635 for i=1,pixelsHeight do
636 for j=1,pixelsWidth do
637 if pixels[i][j][3] ~= "#" then
638 if component.gpu.getBackground() ~= pixels[i][j][1] then component.gpu.setBackground(pixels[i][j][1]) end
639 if component.gpu.getForeground() ~= pixels[i][j][2] then component.gpu.setForeground(pixels[i][j][2]) end
640 component.gpu.set(x+j,y+i,pixels[i][j][3])
641 end
642 end
643 end
644
645 return (x+1),(y+1),xEnd,yEnd
646end
647
648--Корректировка стартовых координат. Core-функция для всех моих программ
649function ecs.correctStartCoords(xStart,yStart,xWindowSize,yWindowSize)
650 local xSize,ySize = component.gpu.getResolution()
651 if xStart == "auto" then
652 xStart = math.floor(xSize/2 - xWindowSize/2)
653 end
654 if yStart == "auto" then
655 yStart = math.ceil(ySize/2 - yWindowSize/2)
656 end
657 return xStart,yStart
658end
659
660--Запомнить область пикселей и возвратить ее в виде массива
661function ecs.rememberOldPixels(x, y, x2, y2)
662 local newPNGMassiv = { ["backgrounds"] = {} }
663 local xSize, ySize = component.gpu.getResolution()
664 newPNGMassiv.x, newPNGMassiv.y = x, y
665
666 --Перебираем весь массив стандартного PNG-вида по высоте
667 local xCounter, yCounter = 1, 1
668 for j = y, y2 do
669 xCounter = 1
670 for i = x, x2 do
671
672 if (i > xSize or i < 0) or (j > ySize or j < 0) then
673 error("Can't remember pixel, because it's located behind the screen: x("..i.."), y("..j..") out of xSize("..xSize.."), ySize("..ySize..")\n")
674 end
675
676 local symbol, fore, back = component.gpu.get(i, j)
677
678 newPNGMassiv["backgrounds"][back] = newPNGMassiv["backgrounds"][back] or {}
679 newPNGMassiv["backgrounds"][back][fore] = newPNGMassiv["backgrounds"][back][fore] or {}
680
681 table.insert(newPNGMassiv["backgrounds"][back][fore], {xCounter, yCounter, symbol} )
682
683 xCounter = xCounter + 1
684 back, fore, symbol = nil, nil, nil
685 end
686
687 yCounter = yCounter + 1
688 end
689
690 xSize, ySize = nil, nil
691 return newPNGMassiv
692end
693
694--Нарисовать запомненные ранее пиксели из массива
695function ecs.drawOldPixels(massivSudaPihay)
696 --Перебираем массив с фонами
697 for back, backValue in pairs(massivSudaPihay["backgrounds"]) do
698 component.gpu.setBackground(back)
699 for fore, foreValue in pairs(massivSudaPihay["backgrounds"][back]) do
700 component.gpu.setForeground(fore)
701 for pixel = 1, #massivSudaPihay["backgrounds"][back][fore] do
702 if massivSudaPihay["backgrounds"][back][fore][pixel][3] ~= transparentSymbol then
703 component.gpu.set(massivSudaPihay.x + massivSudaPihay["backgrounds"][back][fore][pixel][1] - 1, massivSudaPihay.y + massivSudaPihay["backgrounds"][back][fore][pixel][2] - 1, massivSudaPihay["backgrounds"][back][fore][pixel][3])
704 end
705 end
706 end
707 end
708end
709
710--Ограничение длины строки. Маст-хев функция.
711function ecs.stringLimit(mode, text, size, noDots)
712 if unicode.len(text) <= size then return text end
713 local length = unicode.len(text)
714 if mode == "start" then
715 if noDots then
716 return unicode.sub(text, length - size + 1, -1)
717 else
718 return "…" .. unicode.sub(text, length - size + 2, -1)
719 end
720 else
721 if noDots then
722 return unicode.sub(text, 1, size)
723 else
724 return unicode.sub(text, 1, size - 1) .. "…"
725 end
726 end
727end
728
729--Получить текущее реальное время компьютера, хостящего сервер майна
730function ecs.getHostTime(timezone)
731 timezone = timezone or 2
732 --Создаем файл с записанной в него парашей
733 local file = io.open("HostTime.tmp", "w")
734 file:write("")
735 file:close()
736 --Коррекция времени на основе часового пояса
737 local timeCorrection = timezone * 3600
738 --Получаем дату изменения файла в юникс-виде
739 local lastModified = tonumber(string.sub(fs.lastModified("HostTime.tmp"), 1, -4)) + timeCorrection
740 --Удаляем файл, ибо на хуй он нам не нужен
741 fs.remove("HostTime.tmp")
742 --Конвертируем юникс-время в норм время
743 local year, month, day, hour, minute, second = os.date("%Y", lastModified), os.date("%m", lastModified), os.date("%d", lastModified), os.date("%H", lastModified), os.date("%M", lastModified), os.date("%S", lastModified)
744 --Возвращаем все
745 return tonumber(day), tonumber(month), tonumber(year), tonumber(hour), tonumber(minute), tonumber(second)
746end
747
748--Получить спискок файлов из конкретной директории, костыль
749function ecs.getFileList(path)
750 local list = fs.list(path)
751 local massiv = {}
752 for file in list do
753 --if string.find(file, "%/$") then file = unicode.sub(file, 1, -2) end
754 table.insert(massiv, file)
755 end
756 list = nil
757 return massiv
758end
759
760--Получить файловое древо. Сильно нагружает систему, только для дебага!
761function ecs.getFileTree(path)
762 local massiv = {}
763 local list = ecs.getFileList(path)
764 for key, file in pairs(list) do
765 if fs.isDirectory(path.."/"..file) then
766 table.insert(massiv, getFileTree(path.."/"..file))
767 else
768 table.insert(massiv, file)
769 end
770 end
771 list = nil
772
773 return massiv
774end
775
776--Поиск по файловой системе
777function ecs.find(path, cheBudemIskat)
778 --Массив, в котором будут находиться все найденные соответствия
779 local massivNaydennogoGovna = {}
780 --Костыль, но удобный
781 local function dofind(path, cheBudemIskat)
782 --Получаем список файлов в директории
783 local list = ecs.getFileList(path)
784 --Перебираем все элементы файл листа
785 for key, file in pairs(list) do
786 --Путь к файлу
787 local pathToFile = path..file
788 --Если нашло совпадение в имени файла, то выдает путь к этому файлу
789 if string.find(unicode.lower(file), unicode.lower(cheBudemIskat)) then
790 table.insert(massivNaydennogoGovna, pathToFile)
791 end
792 --Анализ, что делать дальше
793 if fs.isDirectory(pathToFile) then
794 dofind(pathToFile, cheBudemIskat)
795 end
796 --Очищаем оперативку
797 pathToFile = nil
798 end
799 --Очищаем оперативку
800 list = nil
801 end
802 --Выполняем функцию
803 dofind(path, cheBudemIskat)
804 --Возвращаем, че нашло
805 return massivNaydennogoGovna
806end
807
808--Получение формата файла
809function ecs.getFileFormat(path)
810 local name = fs.name(path)
811 local starting, ending = string.find(name, "(.)%.[%d%w]*$")
812 if starting == nil then
813 return nil
814 else
815 return unicode.sub(name,starting + 1, -1)
816 end
817 name, starting, ending = nil, nil, nil
818end
819
820--Проверить, скрытый ли файл (.пидор, .хуй = true; пидор, хуй = false)
821function ecs.isFileHidden(path)
822 local name = fs.name(path)
823 local starting, ending = string.find(name, "^%.(.*)$")
824 if starting == nil then
825 return false
826 else
827 return true
828 end
829 name, starting, ending = nil, nil, nil
830end
831
832--Скрыть формат файла
833function ecs.hideFileFormat(path)
834 local name = fs.name(path)
835 local fileFormat = ecs.getFileFormat(name)
836 if fileFormat == nil then
837 return name
838 else
839 return unicode.sub(name, 1, unicode.len(name) - unicode.len(fileFormat))
840 end
841end
842
843--Ожидание клика либо нажатия какой-либо клавиши
844function ecs.waitForTouchOrClick()
845 while true do
846 local e = { event.pull() }
847 if e[1] == "key_down" or e[1] == "touch" then break end
848 end
849end
850
851--То же самое, но в сокращенном варианте
852function ecs.wait()
853 ecs.waitForTouchOrClick()
854end
855
856--Нарисовать кнопочки закрытия окна
857function ecs.drawCloses(x, y, active)
858 local symbol = "⮾"
859 ecs.colorText(x, y , (active == 1 and ecs.colors.blue) or 0xCC4C4C, symbol)
860 ecs.colorText(x + 2, y , (active == 2 and ecs.colors.blue) or 0xDEDE6C, symbol)
861 ecs.colorText(x + 4, y , (active == 3 and ecs.colors.blue) or 0x57A64E, symbol)
862end
863
864--Нарисовать верхнюю оконную панель с выбором объектов
865function ecs.drawTopBar(x, y, width, selectedObject, background, foreground, ...)
866 local objects = { ... }
867 ecs.square(x, y, width, 3, background)
868 local widthOfObjects = 0
869 local spaceBetween = 2
870 for i = 1, #objects do
871 widthOfObjects = widthOfObjects + unicode.len(objects[i][1]) + spaceBetween
872 end
873 local xPos = x + math.floor(width / 2 - widthOfObjects / 2)
874 for i = 1, #objects do
875 if i == selectedObject then
876 ecs.square(xPos, y, unicode.len(objects[i][1]) + spaceBetween, 3, ecs.colors.blue)
877 component.gpu.setForeground(0xffffff)
878 else
879 component.gpu.setBackground(background)
880 component.gpu.setForeground(foreground)
881 end
882 component.gpu.set(xPos + spaceBetween / 2, y + 2, objects[i][1])
883 component.gpu.set(xPos + math.ceil(unicode.len(objects[i][1]) / 2), y + 1, objects[i][2])
884
885 xPos = xPos + unicode.len(objects[i][1]) + spaceBetween
886 end
887end
888
889--Нарисовать топ-меню, горизонтальная полоска такая с текстами
890function ecs.drawTopMenu(x, y, width, color, selectedObject, ...)
891 local objects = { ... }
892 local objectsToReturn = {}
893 local xPos = x + 2
894 local spaceBetween = 2
895 ecs.square(x, y, width, 1, color)
896 for i = 1, #objects do
897 if i == selectedObject then
898 ecs.square(xPos - 1, y, unicode.len(objects[i][1]) + spaceBetween, 1, ecs.colors.blue)
899 component.gpu.setForeground(0xffffff)
900 component.gpu.set(xPos, y, objects[i][1])
901 component.gpu.setForeground(objects[i][2])
902 component.gpu.setBackground(color)
903 else
904 if component.gpu.getForeground() ~= objects[i][2] then component.gpu.setForeground(objects[i][2]) end
905 component.gpu.set(xPos, y, objects[i][1])
906 end
907 objectsToReturn[objects[i][1]] = { xPos, y, xPos + unicode.len(objects[i][1]) - 1, y, i }
908 xPos = xPos + unicode.len(objects[i][1]) + spaceBetween
909 end
910 return objectsToReturn
911end
912
913--Функция отрисовки кнопки указанной ширины
914function ecs.drawButton(x,y,width,height,text,backColor,textColor)
915 x,y = ecs.correctStartCoords(x,y,width,height)
916
917 local textPosX = math.floor(x + width / 2 - unicode.len(text) / 2)
918 local textPosY = math.floor(y + height / 2)
919 ecs.square(x,y,width,height,backColor)
920 ecs.colorText(textPosX,textPosY,textColor,text)
921
922 return x, y, (x + width - 1), (y + height - 1)
923end
924
925--Отрисовка кнопки с указанными отступами от текста
926function ecs.drawAdaptiveButton(x,y,offsetX,offsetY,text,backColor,textColor)
927 local length = unicode.len(text)
928 local width = offsetX*2 + length
929 local height = offsetY*2 + 1
930
931 x,y = ecs.correctStartCoords(x,y,width,height)
932
933 ecs.square(x,y,width,height,backColor)
934 ecs.colorText(x+offsetX,y+offsetY,textColor,text)
935
936 return x,y,(x+width-1),(y+height-1)
937end
938
939--Отрисовка оконной "тени"
940function ecs.windowShadow(x,y,width,height)
941 component.gpu.setBackground(ecs.windowColors.shadow)
942 component.gpu.fill(x+width,y+1,2,height," ")
943 component.gpu.fill(x+1,y+height,width,1," ")
944end
945
946--Просто белое окошко с тенью
947function ecs.blankWindow(x,y,width,height)
948 local oldPixels = ecs.rememberOldPixels(x,y,x+width+1,y+height)
949
950 ecs.square(x,y,width,height,ecs.windowColors.background)
951
952 ecs.windowShadow(x,y,width,height)
953
954 return oldPixels
955end
956
957--Белое окошко, но уже с титлом вверху!
958function ecs.emptyWindow(x,y,width,height,title)
959
960 local oldPixels = ecs.rememberOldPixels(x,y,x+width+1,y+height)
961
962 --ОКНО
963 component.gpu.setBackground(ecs.windowColors.background)
964 component.gpu.fill(x,y+1,width,height-1," ")
965
966 --ТАБ СВЕРХУ
967 component.gpu.setBackground(ecs.windowColors.tab)
968 component.gpu.fill(x,y,width,1," ")
969
970 --ТИТЛ
971 component.gpu.setForeground(ecs.windowColors.title)
972 local textPosX = x + math.floor(width/2-unicode.len(title)/2) -1
973 component.gpu.set(textPosX,y,title)
974
975 --ТЕНЬ
976 ecs.windowShadow(x,y,width,height)
977
978 return oldPixels
979
980end
981
982function ecs.getWordsArrayFromString(s)
983 local words = {}
984 for word in string.gmatch(s, "[^%s]+") do table.insert(words, word) end
985 return words
986end
987
988--Моя любимая функция ошибки C:
989function ecs.error(...)
990 local args = {...}
991 local text = ""
992 if #args > 1 then
993 for i = 1, #args do
994 --text = text .. "[" .. i .. "] = " .. tostring(args[i])
995 if type(args[i]) == "string" then args[i] = "\"" .. args[i] .. "\"" end
996 text = text .. tostring(args[i])
997 if i ~= #args then text = text .. ", " end
998 end
999 else
1000 text = tostring(args[1])
1001 end
1002 ecs.universalWindow("auto", "auto", math.ceil(component.gpu.getResolution() * 0.45), ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x880000, "Ошибка!"}, {"EmptyLine"}, {"WrappedText", 0x262626, text}, {"EmptyLine"}, {"Button", {0x880000, 0xffffff, "OK!"}})
1003end
1004
1005--Очистить экран, установить комфортные цвета и поставить курсок на 1, 1
1006function ecs.prepareToExit(color1, color2)
1007 term.setCursor(1, 1)
1008 ecs.clearScreen(color1 or 0x333333)
1009 component.gpu.setForeground(color2 or 0xffffff)
1010 component.gpu.set(1, 1, "")
1011end
1012
1013--Конвертация из юникода в символ. Вроде норм, а вроде и не норм. Но полезно.
1014function ecs.convertCodeToSymbol(code)
1015 local symbol
1016 if code ~= 0 and code ~= 13 and code ~= 8 and code ~= 9 and code ~= 200 and code ~= 208 and code ~= 203 and code ~= 205 and not keyboard.isControlDown() then
1017 symbol = unicode.char(code)
1018 if keyboard.isShiftPressed then symbol = unicode.upper(symbol) end
1019 end
1020 return symbol
1021end
1022
1023--Шкала прогресса - маст-хев!
1024function ecs.progressBar(x, y, width, height, background, foreground, percent)
1025 local activeWidth = math.ceil(width * percent / 100)
1026 ecs.square(x, y, width, height, background)
1027 ecs.square(x, y, activeWidth, height, foreground)
1028end
1029
1030--Окошко с прогрессбаром! Давно хотел
1031function ecs.progressWindow(x, y, width, percent, text, returnOldPixels)
1032 local height = 6
1033 local barWidth = width - 6
1034
1035 x, y = ecs.correctStartCoords(x, y, width, height)
1036
1037 local oldPixels
1038 if returnOldPixels then
1039 oldPixels = ecs.rememberOldPixels(x, y, x + width + 1, y + height)
1040 end
1041
1042 ecs.emptyWindow(x, y, width, height, " ")
1043 ecs.colorTextWithBack(x + math.floor(width / 2 - unicode.len(text) / 2), y + 4, 0x000000, ecs.windowColors.background, text)
1044 ecs.progressBar(x + 3, y + 2, barWidth, 1, 0xCCCCCC, ecs.colors.blue, percent)
1045
1046 return oldPixels
1047end
1048
1049--Функция для ввода текста в мини-поле.
1050function ecs.inputText(x, y, limit, cheBiloVvedeno, background, foreground, justDrawNotEvent, maskTextWith)
1051 limit = limit or 10
1052 cheBiloVvedeno = cheBiloVvedeno or ""
1053 background = background or 0xffffff
1054 foreground = foreground or 0x000000
1055
1056 component.gpu.setBackground(background)
1057 component.gpu.setForeground(foreground)
1058 component.gpu.fill(x, y, limit, 1, " ")
1059
1060 local text = cheBiloVvedeno
1061
1062 local function draw()
1063 term.setCursorBlink(false)
1064
1065 local dlina = unicode.len(text)
1066 local xCursor = x + dlina
1067 if xCursor > (x + limit - 1) then xCursor = (x + limit - 1) end
1068
1069 if maskTextWith then
1070 component.gpu.set(x, y, ecs.stringLimit("start", string.rep("●", dlina), limit))
1071 else
1072 component.gpu.set(x, y, ecs.stringLimit("start", text, limit))
1073 end
1074
1075 term.setCursor(xCursor, y)
1076
1077 term.setCursorBlink(true)
1078 end
1079
1080 draw()
1081
1082 if justDrawNotEvent then term.setCursorBlink(false); return cheBiloVvedeno end
1083
1084 while true do
1085 local e = {event.pull()}
1086 if e[1] == "key_down" then
1087 if e[4] == 14 then
1088 term.setCursorBlink(false)
1089 text = unicode.sub(text, 1, -2)
1090 if unicode.len(text) < limit then component.gpu.set(x + unicode.len(text), y, " ") end
1091 draw()
1092 elseif e[4] == 28 then
1093 term.setCursorBlink(false)
1094 return text
1095 else
1096 local symbol = ecs.convertCodeToSymbol(e[3])
1097 if symbol then
1098 text = text..symbol
1099 draw()
1100 end
1101 end
1102 elseif e[1] == "touch" then
1103 term.setCursorBlink(false)
1104 return text
1105 elseif e[1] == "clipboard" then
1106 if e[3] then
1107 text = text..e[3]
1108 draw()
1109 end
1110 end
1111 end
1112end
1113
1114--Спросить, заменять ли файл (если таковой уже имеется)
1115function ecs.askForReplaceFile(path, includeForAllButton)
1116 local cyka = {
1117 {"EmptyLine"},
1118 {"CenterText", 0x262626, "Файл \"".. fs.name(path) .. "\" уже имеется в этом месте."},
1119 {"CenterText", 0x262626, "Заменить его перемещаемым объектом?"},
1120 {"EmptyLine"}
1121 }
1122 if includeForAllButton then table.insert(cyka, {"Switch", 0xF2B233, 0xffffff, 0x262626, "Для всех", true}) end
1123 table.insert(cyka, {"Button", {0x444444, 0xFFFFFF, "Заменить"}, {0x666666, 0xFFFFFF, "Отмена"}})
1124
1125 local action = ecs.universalWindow("auto", "auto", 46, ecs.windowColors.background, true, table.unpack(cyka))
1126 if action[includeForAllButton and 2 or 1] == "Заменить" then
1127 return 1, action[includeForAllButton and 1]
1128 else
1129 return 2, action[includeForAllButton and 1]
1130 end
1131end
1132
1133--Проверить имя файла на соответствие критериям
1134function ecs.checkName(name, path)
1135 --Если ввели хуйню какую-то, то
1136 if name == "" or name == " " or name == nil then
1137 ecs.error("Неверное имя файла.")
1138 return false
1139 else
1140 --Если файл с новым путем уже существует, то
1141 if fs.exists(path .. name) then
1142 ecs.error("Файл \"".. name .. "\" уже имеется в этом месте.")
1143 return false
1144 --А если все заебок, то
1145 else
1146 return true
1147 end
1148 end
1149end
1150
1151--Переименование файлов (для операционки)
1152function ecs.rename(mainPath)
1153 --Задаем стартовую щнягу
1154 local name = fs.name(mainPath)
1155 path = fs.path(mainPath)
1156 --Рисуем окошко ввода нового имени файла
1157 local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Переименовать"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, name}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
1158 --Переименовываем
1159 if ecs.checkName(inputs[1], path) then
1160 fs.rename(mainPath, path .. inputs[1])
1161 end
1162end
1163
1164--Создать новую папку (для операционки)
1165function ecs.newFolder(path)
1166 --Рисуем окошко ввода нового имени файла
1167 local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новая папка"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
1168
1169 if ecs.checkName(inputs[1], path) then
1170 fs.makeDirectory(path .. inputs[1])
1171 end
1172end
1173
1174--Создать новый файл (для операционки)
1175function ecs.newFile(path)
1176 --Рисуем окошко ввода нового имени файла
1177 local inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новый файл"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
1178
1179 if ecs.checkName(inputs[1], path) then
1180 local MineOSCore = require("MineOSCore")
1181 MineOSCore.safeLaunch(MineOSCore.paths.applications .. "/MineCode IDE.app/Main.lua", "open", path .. inputs[1])
1182 end
1183end
1184
1185--Создать новое приложение (для операционки)
1186function ecs.newApplication(path, startName)
1187 --Рисуем окошко ввода нового имени файла
1188 local inputs
1189 if not startName then
1190 inputs = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новое приложение"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Введите имя"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
1191 end
1192
1193 if ecs.checkName(inputs[1] .. ".app", path) then
1194 local name = path .. inputs[1] .. ".app/Resources/"
1195 fs.makeDirectory(name)
1196 fs.copy("MineOS/System/OS/Icons/SampleIcon.pic", name .. "Icon.pic")
1197 local file = io.open(path .. inputs[1] .. ".app/Main.lua", "w")
1198 file:write("require('GUI').error('Hello world')")
1199 file:close()
1200 end
1201end
1202
1203--Создать приложение на основе существующего ЛУА-файла
1204function ecs.newApplicationFromLuaFile(pathToLuaFile, pathWhereToCreateApplication)
1205 local data = ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x000000, "Новое приложение"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Имя приложения"}, {"Input", 0x262626, 0x880000, "Путь к иконке приложения"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
1206 data[1] = data[1] or "MyApplication"
1207 data[2] = data[2] or "MineOS/System/OS/Icons/SampleIcon.pic"
1208 if fs.exists(data[2]) then
1209 fs.makeDirectory(pathWhereToCreateApplication .. "/" .. data[1] .. ".app/Resources")
1210 fs.copy(pathToLuaFile, pathWhereToCreateApplication .. "/" .. data[1] .. ".app/" .. data[1] .. ".lua")
1211 fs.copy(data[2], pathWhereToCreateApplication .. "/" .. data[1] .. ".app/Resources/Icon.pic")
1212
1213 --ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x000000, "Приложение создано!"}, {"EmptyLine"}, {"Button", {ecs.colors.green, 0xffffff, "OK"}})
1214 else
1215 ecs.error("Указанный файл иконки не существует.")
1216 return
1217 end
1218end
1219
1220--Простое информационное окошечко. Возвращает старые пиксели - мало ли понадобится.
1221function ecs.info(x, y, title, text)
1222 x = x or "auto"
1223 y = y or "auto"
1224 title = title or " "
1225 text = text or "Sample text"
1226
1227 local width = unicode.len(text) + 4
1228 local height = 4
1229 x, y = ecs.correctStartCoords(x, y, width, height)
1230
1231 local oldPixels = ecs.rememberOldPixels(x, y, x + width + 1, y + height)
1232
1233 ecs.emptyWindow(x, y, width, height, title)
1234 ecs.colorTextWithBack(x + 2, y + 2, ecs.windowColors.usualText, ecs.windowColors.background, text)
1235
1236 return oldPixels
1237end
1238
1239--Вертикальный скроллбар. Маст-хев!
1240function ecs.srollBar(x, y, width, height, countOfAllElements, currentElement, backColor, frontColor)
1241 local sizeOfScrollBar = math.ceil(1 / countOfAllElements * height)
1242 local displayBarFrom = math.floor(y + height * ((currentElement - 1) / countOfAllElements))
1243
1244 ecs.square(x, y, width, height, backColor)
1245 ecs.square(x, displayBarFrom, width, sizeOfScrollBar, frontColor)
1246
1247 sizeOfScrollBar, displayBarFrom = nil, nil
1248end
1249
1250--Отрисовка поля с текстом. Сюда пихать массив вида {"строка1", "строка2", "строка3", ...}
1251function ecs.textField(x, y, width, height, lines, displayFrom, background, foreground, scrollbarBackground, scrollbarForeground)
1252 x, y = ecs.correctStartCoords(x, y, width, height)
1253
1254 background = background or 0xffffff
1255 foreground = foreground or ecs.windowColors.usualText
1256
1257 local sLines = #lines
1258 local lineLimit = width - 3
1259
1260 --Парсим строки
1261 local line = 1
1262 while lines[line] do
1263 local sLine = unicode.len(lines[line])
1264 if sLine > lineLimit then
1265 local part1, part2 = unicode.sub(lines[line], 1, lineLimit), unicode.sub(lines[line], lineLimit + 1, -1)
1266 lines[line] = part1
1267 table.insert(lines, line + 1, part2)
1268 part1, part2 = nil, nil
1269 end
1270 line = line + 1
1271 sLine = nil
1272 end
1273 line = nil
1274
1275 ecs.square(x, y, width - 1, height, background)
1276 ecs.srollBar(x + width - 1, y, 1, height, sLines, displayFrom, scrollbarBackground, scrollbarForeground)
1277
1278 component.gpu.setBackground(background)
1279 component.gpu.setForeground(foreground)
1280 local yPos = y
1281 for i = displayFrom, (displayFrom + height - 1) do
1282 if lines[i] then
1283 component.gpu.set(x + 1, yPos, lines[i])
1284 yPos = yPos + 1
1285 else
1286 break
1287 end
1288 end
1289
1290 return sLines
1291end
1292
1293--Получение верного имени языка. Просто для безопасности. (для операционки)
1294function ecs.getCorrectLangName(pathToLangs)
1295 local language = _G.OSSettings.language .. ".lang"
1296 if not fs.exists(pathToLangs .. "/" .. language) then
1297 language = "English.lang"
1298 end
1299 return language
1300end
1301
1302--Чтение языкового файла (для операционки)
1303function ecs.readCorrectLangFile(pathToLangs)
1304 local lang
1305
1306 local language = ecs.getCorrectLangName(pathToLangs)
1307
1308 lang = config.readAll(pathToLangs .. "/" .. language)
1309
1310 return lang
1311end
1312
1313-------------------------ВСЕ ДЛЯ ОСКИ-------------------------------------------------------------------------------
1314
1315function ecs.searchInArray(array, textToSearch)
1316 local newArray = {}
1317 for i = 1, #array do
1318 if string.find(unicode.lower(array[i]), unicode.lower(textToSearch)) then table.insert(newArray, array[i]) end
1319 end
1320 return newArray
1321end
1322
1323function ecs.sortFiles(path, fileList, sortingMethod, showHiddenFiles)
1324 local sortedFileList = {}
1325 if sortingMethod == "type" or sortingMethod == 0 then
1326 local typeList = {}
1327 for i = 1, #fileList do
1328 local fileFormat = ecs.getFileFormat(fileList[i]) or "Script"
1329 if fs.isDirectory(path .. fileList[i]) and fileFormat ~= ".app" then fileFormat = "Folder" end
1330 typeList[fileFormat] = typeList[fileFormat] or {}
1331 table.insert(typeList[fileFormat], fileList[i])
1332 end
1333
1334 if typeList["Folder"] then
1335 for i = 1, #typeList["Folder"] do
1336 table.insert(sortedFileList, typeList["Folder"][i])
1337 end
1338 typeList["Folder"] = nil
1339 end
1340
1341 for fileFormat in pairs(typeList) do
1342 for i = 1, #typeList[fileFormat] do
1343 table.insert(sortedFileList, typeList[fileFormat][i])
1344 end
1345 end
1346 elseif sortingMethod == "name" or sortingMethod == 1 then
1347 sortedFileList = fileList
1348 elseif sortingMethod == "date" or sortingMethod == 2 then
1349 for i = 1, #fileList do
1350 fileList[i] = {fileList[i], fs.lastModified(path .. fileList[i])}
1351 end
1352 table.sort(fileList, function(a,b) return a[2] > b[2] end)
1353 for i = 1, #fileList do
1354 table.insert(sortedFileList, fileList[i][1])
1355 end
1356 else
1357 error("Unknown sorting method: " .. tostring(sortingMethod))
1358 end
1359
1360 local i = 1
1361 while i <= #sortedFileList do
1362 if not showHiddenFiles and ecs.isFileHidden(sortedFileList[i]) then
1363 table.remove(sortedFileList, i)
1364 else
1365 i = i + 1
1366 end
1367 end
1368
1369 return sortedFileList
1370end
1371
1372--Сохранить файл конфигурации ОС
1373function ecs.saveOSSettings()
1374 local pathToOSSettings = "MineOS/System/OS/OSSettings.cfg"
1375 if not _G.OSSettings then error("Массив настроек ОС отсутствует в памяти!") end
1376 fs.makeDirectory(fs.path(pathToOSSettings))
1377 local file = io.open(pathToOSSettings, "w")
1378 file:write(serialization.serialize(_G.OSSettings))
1379 file:close()
1380end
1381
1382--Загрузить файл конфигурации ОС, а если его не существует, то создать
1383function ecs.loadOSSettings()
1384 local pathToOSSettings = "MineOS/System/OS/OSSettings.cfg"
1385 if fs.exists(pathToOSSettings) then
1386 local file = io.open(pathToOSSettings, "r")
1387 _G.OSSettings = serialization.unserialize(file:read("*a"))
1388 file:close()
1389 else
1390 _G.OSSettings = { showHelpOnApplicationStart = true, language = "Russian" }
1391 ecs.saveOSSettings()
1392 end
1393end
1394
1395--Отобразить окно с содержимым файла информации о приложении
1396function ecs.applicationHelp(pathToApplication)
1397 local pathToAboutFile = pathToApplication .. "/resources/About/" .. _G.OSSettings.language .. ".txt"
1398 if _G.OSSettings and _G.OSSettings.showHelpOnApplicationStart and fs.exists(pathToAboutFile) then
1399 local applicationName = fs.name(pathToApplication)
1400 local file = io.open(pathToAboutFile, "r")
1401 local text = ""
1402 for line in file:lines() do text = text .. line .. " " end
1403 file:close()
1404
1405 local data = ecs.universalWindow("auto", "auto", 30, 0xeeeeee, true,
1406 {"EmptyLine"},
1407 {"CenterText", 0x000000, "О приложении " .. applicationName},
1408 {"EmptyLine"},
1409 {"TextField", 16, 0xFFFFFF, 0x262626, 0xcccccc, 0x353535, text},
1410 {"EmptyLine"},
1411 {"Button", {ecs.colors.orange, 0x262626, "OK"}, {0x999999, 0xffffff, "Больше не показывать"}}
1412 )
1413 if data[1] ~= "OK" then
1414 _G.OSSettings.showHelpOnApplicationStart = false
1415 ecs.saveOSSettings()
1416 end
1417 end
1418end
1419
1420function ecs.correctFileNameIfFileExists(path, requestedName)
1421 local number = 1
1422 local fileFormat = ecs.getFileFormat(requestedName) or ""
1423 requestedName = ecs.hideFileFormat(requestedName)
1424 while true do
1425 local finalFileName = requestedName .. string.rep("-copy", number) .. fileFormat
1426 if fs.exists(path .. "/" .. finalFileName) then
1427 number = number + 1
1428 else
1429 return finalFileName
1430 end
1431 end
1432end
1433
1434--Создать ярлык для конкретной проги (для операционки)
1435function ecs.createShortCut(pathToShortcut, pathToFile)
1436 local pathToPathToShortcut = fs.path(pathToShortcut) or "/"
1437 fs.makeDirectory(pathToPathToShortcut)
1438 if fs.exists(pathToShortcut) then
1439 pathToShortcut = ecs.correctFileNameIfFileExists(pathToPathToShortcut, pathToShortcut)
1440 end
1441
1442 local file = io.open(pathToShortcut, "w")
1443 file:write("return ", "\"", pathToFile, "\"")
1444 file:close()
1445end
1446
1447--Получить данные о файле из ярлыка (для операционки)
1448function ecs.readShortcut(path)
1449 local success, filename = pcall(loadfile(path))
1450 if success then
1451 return filename
1452 else
1453 error("Ошибка чтения файла ярлыка. Вероятно, он создан криво, либо не существует в папке \"" .. fs.path(path) or "" .. "\"")
1454 end
1455end
1456
1457--Редактирование файла (для операционки)
1458function ecs.editFile(path)
1459 ecs.prepareToExit()
1460 shell.execute("edit " .. path)
1461end
1462
1463--Копирование файлов и папок
1464function ecs.copy(from, toFolder)
1465 fs.makeDirectory(toFolder)
1466
1467 if fs.isDirectory(from) then
1468 local currentAction, yesToAllAction
1469 local function recursiveFolderCopy(from, to)
1470 for file in fs.list(from) do
1471 local finalFromName = from .. "/" .. file
1472 local finalToName = to .. "/" .. file
1473
1474 if fs.exists(finalToName) then
1475 if not yesToAllAction then
1476 currentAction, yesToAll = ecs.askForReplaceFile(finalToName, true)
1477 if yesToAll == true then yesToAllAction = true end
1478 end
1479 else
1480 currentAction = 1
1481 end
1482
1483 if currentAction == 1 then
1484 if fs.isDirectory(finalFromName) then
1485 fs.makeDirectory(finalToName)
1486 recursiveFolderCopy(finalFromName, finalToName)
1487 else
1488 fs.copy(finalFromName, finalToName)
1489 end
1490 end
1491 end
1492 end
1493
1494 recursiveFolderCopy(from, toFolder .. fs.name(from))
1495 else
1496 local to = toFolder .. "/" .. fs.name(from)
1497 local action = 1
1498 if fs.exists(to) then action = ecs.askForReplaceFile(to) end
1499 if action == 1 then fs.copy(from, to) end
1500 end
1501end
1502
1503-- Анимация затухания экрана
1504function ecs.fadeOut(startColor, targetColor, speed)
1505 local xSize, ySize = component.gpu.getResolution()
1506 while startColor >= targetColor do
1507 component.gpu.setBackground(startColor)
1508 component.gpu.fill(1, 1, xSize, ySize, " ")
1509 startColor = startColor - 0x111111
1510 os.sleep(speed or 0)
1511 end
1512end
1513
1514-- Анимация загорания экрана
1515function ecs.fadeIn(startColor, targetColor, speed)
1516 local xSize, ySize = component.gpu.getResolution()
1517 while startColor <= targetColor do
1518 component.gpu.setBackground(startColor)
1519 component.gpu.fill(1, 1, xSize, ySize, " ")
1520 startColor = startColor + 0x111111
1521 os.sleep(speed or 0)
1522 end
1523end
1524
1525-- Анимация выхода в олдскул-телевизионном стиле
1526function ecs.TV(speed, targetColor)
1527 local xSize, ySize = component.gpu.getResolution()
1528 local xCenter, yCenter = math.floor(xSize / 2), math.floor(ySize / 2)
1529 component.gpu.setBackground(targetColor or 0x000000)
1530
1531 for y = 1, yCenter do
1532 component.gpu.fill(1, y - 1, xSize, 1, " ")
1533 component.gpu.fill(1, ySize - y + 1, xSize, 1, " ")
1534 os.sleep(speed or 0)
1535 end
1536
1537 for x = 1, xCenter - 1 do
1538 component.gpu.fill(x, yCenter, 1, 1, " ")
1539 component.gpu.fill(xSize - x + 1, yCenter, 1, 1, " ")
1540 os.sleep(speed or 0)
1541 end
1542 os.sleep(0.3)
1543 component.gpu.fill(1, yCenter, xSize, 1, " ")
1544end
1545
1546
1547
1548---------------------------------------------ОКОШЕЧКИ------------------------------------------------------------
1549
1550
1551--Описание ниже, ебана. Ниже - это значит в самой жопе кода!
1552function ecs.universalWindow(x, y, width, background, closeWindowAfter, ...)
1553 local objects = {...}
1554 local countOfObjects = #objects
1555
1556 local pressedButton
1557 local pressedMultiButton
1558
1559 --Задаем высотные константы для объектов
1560 local objectsHeights = {
1561 ["button"] = 3,
1562 ["centertext"] = 1,
1563 ["emptyline"] = 1,
1564 ["input"] = 3,
1565 ["slider"] = 3,
1566 ["select"] = 3,
1567 ["selector"] = 3,
1568 ["separator"] = 1,
1569 ["switch"] = 1,
1570 ["color"] = 3,
1571 }
1572
1573 --Скорректировать ширину, если нужно
1574 local function correctWidth(newWidthForAnalyse)
1575 width = math.max(width, newWidthForAnalyse)
1576 end
1577
1578 --Корректируем ширину
1579 for i = 1, countOfObjects do
1580 local objectType = string.lower(objects[i][1])
1581
1582 if objectType == "centertext" then
1583 correctWidth(unicode.len(objects[i][3]) + 2)
1584 elseif objectType == "slider" then --!!!!!!!!!!!!!!!!!! ВОТ ТУТ НЕ ЗАБУДЬ ФИКСАНУТЬ
1585 correctWidth(unicode.len(objects[i][7]..tostring(objects[i][5].." ")) + 2)
1586 elseif objectType == "select" then
1587 for j = 4, #objects[i] do
1588 correctWidth(unicode.len(objects[i][j]) + 2)
1589 end
1590 --elseif objectType == "selector" then
1591
1592 --elseif objectType == "separator" then
1593
1594 elseif objectType == "textfield" then
1595 correctWidth(5)
1596 elseif objectType == "wrappedtext" then
1597 correctWidth(6)
1598 elseif objectType == "button" then
1599 --Корректируем ширину
1600 local widthOfButtons = 0
1601 local maxButton = 0
1602 for j = 2, #objects[i] do
1603 maxButton = math.max(maxButton, unicode.len(objects[i][j][3]) + 2)
1604 end
1605 widthOfButtons = maxButton * #objects[i]
1606 correctWidth(widthOfButtons)
1607 elseif objectType == "switch" then
1608 local dlina = unicode.len(objects[i][5]) + 2 + 10 + 4
1609 correctWidth(dlina)
1610 elseif objectType == "color" then
1611 correctWidth(unicode.len(objects[i][2]) + 6)
1612 end
1613 end
1614
1615 --Считаем высоту этой хуйни
1616 local height = 0
1617 for i = 1, countOfObjects do
1618 local objectType = string.lower(objects[i][1])
1619 if objectType == "select" then
1620 height = height + (objectsHeights[objectType] * (#objects[i] - 3))
1621 elseif objectType == "textfield" then
1622 height = height + objects[i][2]
1623 elseif objectType == "wrappedtext" then
1624 --Заранее парсим текст перенесенный
1625 objects[i].wrapped = string.wrap({objects[i][3]}, width - 4)
1626 objects[i].height = #objects[i].wrapped
1627 height = height + objects[i].height
1628 else
1629 height = height + objectsHeights[objectType]
1630 end
1631 end
1632
1633 --Коорректируем стартовые координаты
1634 x, y = ecs.correctStartCoords(x, y, width, height)
1635 --Запоминаем инфу о том, что было нарисовано, если это необходимо
1636 local oldPixels, oldBackground, oldForeground
1637 if closeWindowAfter then
1638 oldBackground = component.gpu.getBackground()
1639 oldForeground = component.gpu.getForeground()
1640 oldPixels = ecs.rememberOldPixels(x, y, x + width - 1, y + height - 1)
1641 end
1642 --Считаем все координаты объектов
1643 objects[1].y = y
1644 if countOfObjects > 1 then
1645 for i = 2, countOfObjects do
1646 local objectType = string.lower(objects[i - 1][1])
1647 if objectType == "select" then
1648 objects[i].y = objects[i - 1].y + (objectsHeights[objectType] * (#objects[i - 1] - 3))
1649 elseif objectType == "textfield" then
1650 objects[i].y = objects[i - 1].y + objects[i - 1][2]
1651 elseif objectType == "wrappedtext" then
1652 objects[i].y = objects[i - 1].y + objects[i - 1].height
1653 else
1654 objects[i].y = objects[i - 1].y + objectsHeights[objectType]
1655 end
1656 end
1657 end
1658
1659 --Объекты для тача
1660 local obj = {}
1661 local function newObj(class, name, ...)
1662 obj[class] = obj[class] or {}
1663 obj[class][name] = {...}
1664 end
1665
1666 --Отображение объекта по номеру
1667 local function displayObject(number, active)
1668 local objectType = string.lower(objects[number][1])
1669
1670 if objectType == "centertext" then
1671 local xPos = x + math.floor(width / 2 - unicode.len(objects[number][3]) / 2)
1672 component.gpu.setForeground(objects[number][2])
1673 component.gpu.setBackground(background)
1674 component.gpu.set(xPos, objects[number].y, objects[number][3])
1675
1676 elseif objectType == "input" then
1677
1678 if active then
1679 --Рамочка
1680 ecs.border(x + 1, objects[number].y, width - 2, objectsHeights.input, background, objects[number][3])
1681 --Тестик
1682 objects[number][4] = ecs.inputText(x + 3, objects[number].y + 1, width - 6, "", background, objects[number][3], false, objects[number][5])
1683 else
1684 --Рамочка
1685 ecs.border(x + 1, objects[number].y, width - 2, objectsHeights.input, background, objects[number][2])
1686 --Текстик
1687 component.gpu.set(x + 3, objects[number].y + 1, ecs.stringLimit("start", objects[number][4], width - 6))
1688 ecs.inputText(x + 3, objects[number].y + 1, width - 6, objects[number][4], background, objects[number][2], true, objects[number][5])
1689 end
1690
1691 newObj("Inputs", number, x + 1, objects[number].y, x + width - 2, objects[number].y + 2)
1692
1693 elseif objectType == "slider" then
1694 local widthOfSlider = width - 2
1695 local xOfSlider = x + 1
1696 local yOfSlider = objects[number].y + 1
1697 local countOfSliderThings = objects[number][5] - objects[number][4]
1698 local showSliderValue= objects[number][7]
1699
1700 local dolya = widthOfSlider / countOfSliderThings
1701 local position = math.floor(dolya * objects[number][6])
1702 --Костыль
1703 if (xOfSlider + position) > (xOfSlider + widthOfSlider - 1) then position = widthOfSlider - 2 end
1704
1705 --Две линии
1706 ecs.separator(xOfSlider, yOfSlider, position, background, objects[number][3])
1707 ecs.separator(xOfSlider + position, yOfSlider, widthOfSlider - position, background, objects[number][2])
1708 --Слудир
1709 ecs.square(xOfSlider + position, yOfSlider, 2, 1, objects[number][3])
1710
1711 --Текстик под слудиром
1712 if showSliderValue then
1713 local text = showSliderValue .. tostring(objects[number][6]) .. (objects[number][8] or "")
1714 local textPos = (xOfSlider + widthOfSlider / 2 - unicode.len(text) / 2)
1715 ecs.square(x, yOfSlider + 1, width, 1, background)
1716 ecs.colorText(textPos, yOfSlider + 1, objects[number][2], text)
1717 end
1718
1719 newObj("Sliders", number, xOfSlider, yOfSlider, x + widthOfSlider, yOfSlider, dolya)
1720
1721 elseif objectType == "select" then
1722 local usualColor = objects[number][2]
1723 local selectionColor = objects[number][3]
1724
1725 objects[number].selectedData = objects[number].selectedData or 1
1726
1727 local symbol = "✔"
1728 local yPos = objects[number].y
1729 for i = 4, #objects[number] do
1730 --Коробка для галочки
1731 ecs.border(x + 1, yPos, 5, 3, background, usualColor)
1732 --Текст
1733 component.gpu.set(x + 7, yPos + 1, objects[number][i])
1734 --Галочка
1735 if objects[number].selectedData == (i - 3) then
1736 ecs.colorText(x + 3, yPos + 1, selectionColor, symbol)
1737 else
1738 component.gpu.set(x + 3, yPos + 1, " ")
1739 end
1740
1741 obj["Selects"] = obj["Selects"] or {}
1742 obj["Selects"][number] = obj["Selects"][number] or {}
1743 obj["Selects"][number][i - 3] = { x + 1, yPos, x + width - 2, yPos + 2 }
1744
1745 yPos = yPos + objectsHeights.select
1746 end
1747
1748 elseif objectType == "selector" then
1749 local borderColor = objects[number][2]
1750 local arrowColor = objects[number][3]
1751 local selectorWidth = width - 2
1752 objects[number].selectedElement = objects[number].selectedElement or objects[number][4]
1753
1754 local topLine = "┌" .. string.rep("─", selectorWidth - 6) .. "┬───┐"
1755 local midLine = "│" .. string.rep(" ", selectorWidth - 6) .. "│ │"
1756 local botLine = "└" .. string.rep("─", selectorWidth - 6) .. "┴───┘"
1757
1758 local yPos = objects[number].y
1759
1760 local function bordak(borderColor)
1761 component.gpu.setBackground(background)
1762 component.gpu.setForeground(borderColor)
1763 component.gpu.set(x + 1, objects[number].y, topLine)
1764 component.gpu.set(x + 1, objects[number].y + 1, midLine)
1765 component.gpu.set(x + 1, objects[number].y + 2, botLine)
1766 component.gpu.set(x + 3, objects[number].y + 1, ecs.stringLimit("start", objects[number].selectedElement, width - 6))
1767 ecs.colorText(x + width - 4, objects[number].y + 1, arrowColor, "▼")
1768 end
1769
1770 bordak(borderColor)
1771
1772 --Выпадающий список, самый гемор, блядь
1773 if active then
1774 local xPos, yPos = x + 1, objects[number].y + 3
1775 local spisokWidth = width - 2
1776 local countOfElements = #objects[number] - 3
1777 local spisokHeight = countOfElements + 1
1778 local oldPixels = ecs.rememberOldPixels( xPos, yPos, xPos + spisokWidth - 1, yPos + spisokHeight - 1)
1779
1780 local coords = {}
1781
1782 bordak(arrowColor)
1783
1784 --Рамку рисуем поверх фоника
1785 local topLine = "├"..string.rep("─", spisokWidth - 6).."┴───┤"
1786 local midLine = "│"..string.rep(" ", spisokWidth - 2).."│"
1787 local botLine = "└"..string.rep("─", selectorWidth - 2) .. "┘"
1788 ecs.colorTextWithBack(xPos, yPos - 1, arrowColor, background, topLine)
1789 for i = 1, spisokHeight - 1 do
1790 component.gpu.set(xPos, yPos + i - 1, midLine)
1791 end
1792 component.gpu.set(xPos, yPos + spisokHeight - 1, botLine)
1793
1794 --Элементы рисуем
1795 xPos = xPos + 2
1796 for i = 1, countOfElements do
1797 ecs.colorText(xPos, yPos, borderColor, ecs.stringLimit("start", objects[number][i + 3], spisokWidth - 4))
1798 coords[i] = {xPos - 1, yPos, xPos + spisokWidth - 4, yPos}
1799 yPos = yPos + 1
1800 end
1801
1802 --Обработка
1803 local exit
1804 while true do
1805 if exit then break end
1806 local e = {event.pull()}
1807 if e[1] == "touch" then
1808 for i = 1, #coords do
1809 if ecs.clickedAtArea(e[3], e[4], coords[i][1], coords[i][2], coords[i][3], coords[i][4]) then
1810 ecs.square(coords[i][1], coords[i][2], spisokWidth - 2, 1, ecs.colors.blue)
1811 ecs.colorText(coords[i][1] + 1, coords[i][2], 0xffffff, objects[number][i + 3])
1812 os.sleep(0.3)
1813 objects[number].selectedElement = objects[number][i + 3]
1814 exit = true
1815 break
1816 end
1817 end
1818 end
1819 end
1820
1821 ecs.drawOldPixels(oldPixels)
1822 end
1823
1824 newObj("Selectors", number, x + 1, objects[number].y, x + width - 2, objects[number].y + 2)
1825
1826 elseif objectType == "separator" then
1827 ecs.separator(x, objects[number].y, width, background, objects[number][2])
1828
1829 elseif objectType == "textfield" then
1830 newObj("TextFields", number, x + 1, objects[number].y, x + width - 2, objects[number].y + objects[number][2] - 1)
1831 if not objects[number].strings then objects[number].strings = string.wrap({objects[number][7]}, width - 3) end
1832 objects[number].displayFrom = objects[number].displayFrom or 1
1833 ecs.textField(x, objects[number].y, width, objects[number][2], objects[number].strings, objects[number].displayFrom, objects[number][3], objects[number][4], objects[number][5], objects[number][6])
1834
1835 elseif objectType == "wrappedtext" then
1836 component.gpu.setBackground(background)
1837 component.gpu.setForeground(objects[number][2])
1838 for i = 1, #objects[number].wrapped do
1839 component.gpu.set(x + 2, objects[number].y + i - 1, objects[number].wrapped[i])
1840 end
1841
1842 elseif objectType == "button" then
1843
1844 obj["MultiButtons"] = obj["MultiButtons"] or {}
1845 obj["MultiButtons"][number] = {}
1846
1847 local widthOfButton = math.floor(width / (#objects[number] - 1))
1848
1849 local xPos, yPos = x, objects[number].y
1850 for i = 1, #objects[number] do
1851 if type(objects[number][i]) == "table" then
1852 local x1, y1, x2, y2 = ecs.drawButton(xPos, yPos, widthOfButton, 3, objects[number][i][3], objects[number][i][1], objects[number][i][2])
1853 table.insert(obj["MultiButtons"][number], {x1, y1, x2, y2, widthOfButton})
1854 xPos = x2 + 1
1855
1856 if i == #objects[number] then
1857 ecs.square(xPos, yPos, x + width - xPos, 3, objects[number][i][1])
1858 obj["MultiButtons"][number][i - 1][5] = obj["MultiButtons"][number][i - 1][5] + x + width - xPos
1859 end
1860
1861 x1, y1, x2, y2 = nil, nil, nil, nil
1862 end
1863 end
1864
1865 elseif objectType == "switch" then
1866
1867 local xPos, yPos = x + 2, objects[number].y
1868 local activeColor, passiveColor, textColor, text, state = objects[number][2], objects[number][3], objects[number][4], objects[number][5], objects[number][6]
1869 local switchWidth = 8
1870 ecs.colorTextWithBack(xPos, yPos, textColor, background, text)
1871
1872 xPos = x + width - switchWidth - 2
1873 if state then
1874 ecs.square(xPos, yPos, switchWidth, 1, activeColor)
1875 ecs.square(xPos + switchWidth - 2, yPos, 2, 1, passiveColor)
1876 --ecs.colorTextWithBack(xPos + 4, yPos, passiveColor, activeColor, "ON")
1877 else
1878 ecs.square(xPos, yPos, switchWidth, 1, passiveColor - 0x444444)
1879 ecs.square(xPos, yPos, 2, 1, passiveColor)
1880 --ecs.colorTextWithBack(xPos + 4, yPos, passiveColor, passiveColor - 0x444444, "OFF")
1881 end
1882 newObj("Switches", number, xPos, yPos, xPos + switchWidth - 1, yPos)
1883
1884 elseif objectType == "color" then
1885 local xPos, yPos = x + 1, objects[number].y
1886 local blendedColor = require("color").blend(objects[number][3], 0xFFFFFF, 0.705882)
1887 local w = width - 2
1888
1889 ecs.colorTextWithBack(xPos, yPos + 2, blendedColor, background, string.rep("▀", w))
1890 ecs.colorText(xPos, yPos, objects[number][3], string.rep("▄", w))
1891 ecs.square(xPos, yPos + 1, w, 1, objects[number][3])
1892
1893 ecs.colorText(xPos + 1, yPos + 1, 0xffffff - objects[number][3], objects[number][2])
1894 newObj("Colors", number, xPos, yPos, x + width - 2, yPos + 2)
1895 end
1896 end
1897
1898 --Отображение всех объектов
1899 local function displayAllObjects()
1900 for i = 1, countOfObjects do
1901 displayObject(i)
1902 end
1903 end
1904
1905 --Подготовить массив возвращаемый
1906 local function getReturn()
1907 local massiv = {}
1908
1909 for i = 1, countOfObjects do
1910 local type = string.lower(objects[i][1])
1911
1912 if type == "button" then
1913 table.insert(massiv, pressedButton)
1914 elseif type == "input" then
1915 table.insert(massiv, objects[i][4])
1916 elseif type == "select" then
1917 table.insert(massiv, objects[i][objects[i].selectedData + 3])
1918 elseif type == "selector" then
1919 table.insert(massiv, objects[i].selectedElement)
1920 elseif type == "slider" then
1921 table.insert(massiv, objects[i][6])
1922 elseif type == "switch" then
1923 table.insert(massiv, objects[i][6])
1924 elseif type == "color" then
1925 table.insert(massiv, objects[i][3])
1926 else
1927 table.insert(massiv, nil)
1928 end
1929 end
1930
1931 return massiv
1932 end
1933
1934 local function redrawBeforeClose()
1935 if closeWindowAfter then
1936 ecs.drawOldPixels(oldPixels)
1937 component.gpu.setBackground(oldBackground)
1938 component.gpu.setForeground(oldForeground)
1939 end
1940 end
1941
1942 --Рисуем окно
1943 ecs.square(x, y, width, height, background)
1944 displayAllObjects()
1945
1946 while true do
1947 local e = {event.pull()}
1948 if e[1] == "touch" or e[1] == "drag" then
1949
1950 --Анализируем клик на кнопки
1951 if obj["MultiButtons"] then
1952 for key in pairs(obj["MultiButtons"]) do
1953 for i = 1, #obj["MultiButtons"][key] do
1954 if ecs.clickedAtArea(e[3], e[4], obj["MultiButtons"][key][i][1], obj["MultiButtons"][key][i][2], obj["MultiButtons"][key][i][3], obj["MultiButtons"][key][i][4]) then
1955 ecs.drawButton(obj["MultiButtons"][key][i][1], obj["MultiButtons"][key][i][2], obj["MultiButtons"][key][i][5], 3, objects[key][i + 1][3], objects[key][i + 1][2], objects[key][i + 1][1])
1956 os.sleep(0.3)
1957 pressedButton = objects[key][i + 1][3]
1958 redrawBeforeClose()
1959 return getReturn()
1960 end
1961 end
1962 end
1963 end
1964
1965 --А теперь клик на инпуты!
1966 if obj["Inputs"] then
1967 for key in pairs(obj["Inputs"]) do
1968 if ecs.clickedAtArea(e[3], e[4], obj["Inputs"][key][1], obj["Inputs"][key][2], obj["Inputs"][key][3], obj["Inputs"][key][4]) then
1969 displayObject(key, true)
1970 displayObject(key)
1971 break
1972 end
1973 end
1974 end
1975
1976 --А теперь галочковыбор!
1977 if obj["Selects"] then
1978 for key in pairs(obj["Selects"]) do
1979 for i in pairs(obj["Selects"][key]) do
1980 if ecs.clickedAtArea(e[3], e[4], obj["Selects"][key][i][1], obj["Selects"][key][i][2], obj["Selects"][key][i][3], obj["Selects"][key][i][4]) then
1981 objects[key].selectedData = i
1982 displayObject(key)
1983 break
1984 end
1985 end
1986 end
1987 end
1988
1989 --Хм, а вот и селектор подъехал!
1990 if obj["Selectors"] then
1991 for key in pairs(obj["Selectors"]) do
1992 if ecs.clickedAtArea(e[3], e[4], obj["Selectors"][key][1], obj["Selectors"][key][2], obj["Selectors"][key][3], obj["Selectors"][key][4]) then
1993 displayObject(key, true)
1994 displayObject(key)
1995 break
1996 end
1997 end
1998 end
1999
2000 --Слайдеры, епта! "Потный матан", все делы
2001 if obj["Sliders"] then
2002 for key in pairs(obj["Sliders"]) do
2003 if ecs.clickedAtArea(e[3], e[4], obj["Sliders"][key][1], obj["Sliders"][key][2], obj["Sliders"][key][3], obj["Sliders"][key][4]) then
2004 local xOfSlider, dolya = obj["Sliders"][key][1], obj["Sliders"][key][5]
2005 local currentPixels = e[3] - xOfSlider
2006 local currentValue = math.floor(currentPixels / dolya)
2007 --Костыль
2008 if e[3] == obj["Sliders"][key][3] then currentValue = objects[key][5] end
2009 objects[key][6] = currentValue or objects[key][6]
2010 displayObject(key)
2011 break
2012 end
2013 end
2014 end
2015
2016 if obj["Switches"] then
2017 for key in pairs(obj["Switches"]) do
2018 if ecs.clickedAtArea(e[3], e[4], obj["Switches"][key][1], obj["Switches"][key][2], obj["Switches"][key][3], obj["Switches"][key][4]) then
2019 objects[key][6] = not objects[key][6]
2020 displayObject(key)
2021 break
2022 end
2023 end
2024 end
2025
2026 if obj["Colors"] then
2027 for key in pairs(obj["Colors"]) do
2028 if ecs.clickedAtArea(e[3], e[4], obj["Colors"][key][1], obj["Colors"][key][2], obj["Colors"][key][3], obj["Colors"][key][4]) then
2029 local oldColor = objects[key][3]
2030 objects[key][3] = 0xffffff - objects[key][3]
2031 displayObject(key)
2032 os.sleep(0.3)
2033 objects[key][3] = oldColor
2034 displayObject(key)
2035
2036 local paletteWidth, paletteHeight = 75, 27
2037 local screenWidth, screenHeight = component.gpu.getResolution()
2038 local paletteX, paletteY = math.floor(screenWidth / 2 - paletteWidth / 2), math.floor(screenHeight / 2 - paletteHeight / 2)
2039 local oldPixels = ecs.rememberOldPixels(paletteX, paletteY, paletteX + paletteWidth - 1, paletteY + paletteHeight - 1)
2040 local color = require("palette").show(paletteX, paletteY, objects[key][3])
2041 ecs.drawOldPixels(oldPixels)
2042 objects[key][3] = color or oldColor
2043
2044 displayObject(key)
2045 break
2046 end
2047 end
2048 end
2049
2050 elseif e[1] == "scroll" then
2051 if obj["TextFields"] then
2052 for key in pairs(obj["TextFields"]) do
2053 if ecs.clickedAtArea(e[3], e[4], obj["TextFields"][key][1], obj["TextFields"][key][2], obj["TextFields"][key][3], obj["TextFields"][key][4]) then
2054 if e[5] == 1 then
2055 if objects[key].displayFrom > 1 then objects[key].displayFrom = objects[key].displayFrom - 1; displayObject(key) end
2056 else
2057 if objects[key].displayFrom < #objects[key].strings then objects[key].displayFrom = objects[key].displayFrom + 1; displayObject(key) end
2058 end
2059 end
2060 end
2061 end
2062 elseif e[1] == "key_down" then
2063 if e[4] == 28 then
2064 redrawBeforeClose()
2065 return getReturn()
2066 end
2067 end
2068 end
2069end
2070
2071--Демонстрационное окно, показывающее всю мощь universalWindow
2072function ecs.demoWindow()
2073 --Очищаем экран перед юзанием окна и ставим курсор на 1, 1
2074 ecs.prepareToExit()
2075 --Рисуем окно и получаем данные после взаимодействия с ним
2076 local data = ecs.universalWindow("auto", "auto", 36, 0xeeeeee, true,
2077 {"EmptyLine"},
2078 {"CenterText", 0x880000, "Здорово, ебана!"},
2079 {"EmptyLine"},
2080 {"Input", 0x262626, 0x880000, "Сюда вводить можно"},
2081 {"Selector", 0x262626, 0x880000, "Выбор формата", "PNG", "JPG", "GIF", "PSD"},
2082 {"EmptyLine"},
2083 {"WrappedText", 0x262626, "Тест автоматического переноса букв в зависимости от ширины данного окна. Пока что тупо режет на куски, не особо красиво."},
2084 {"EmptyLine"},
2085 {"Select", 0x262626, 0x880000, "Я пидор", "Я не пидор"},
2086 {"Slider", 0x262626, 0x880000, 1, 100, 50, "Убито ", " младенцев"},
2087 {"EmptyLine"},
2088 {"Separator", 0xaaaaaa},
2089 {"Switch", 0xF2B233, 0xffffff, 0x262626, "✈ Авиарежим", false},
2090 {"EmptyLine"},
2091 {"Switch", 0x3366CC, 0xffffff, 0x262626, "☾ Не беспокоить", true},
2092 {"Separator", 0xaaaaaa},
2093 {"EmptyLine"},
2094 {"TextField", 5, 0xffffff, 0x262626, 0xcccccc, 0x3366CC, "Тест текстового информационного поля. По сути это тот же самый WrappedText, разве что эта хрень ограничена по высоте, и ее можно скроллить. Ну же, поскролль меня! Скролль меня полностью! Моя жадная пизда жаждет твой хуй!"},
2095 {"Color", "Цвет фона", 0xFF0000},
2096 {"EmptyLine"},
2097 {"Button", {0x57A64E, 0xffffff, "Да"}, {0xF2B233, 0xffffff, "Нет"}, {0xCC4C4C, 0xffffff, "Отмена"}}
2098 )
2099 --Еще разок
2100 ecs.prepareToExit()
2101 --Выводим данные
2102 print(" ")
2103 print("Вывод данных из окна:")
2104 for i = 1, #data do print("["..i.."] = "..tostring(data[i])) end
2105 print(" ")
2106end
2107
2108-- ecs.demoWindow()
2109
2110--[[
2111Функция universalWindow(x, y, width, background, closeWindowAfter, ...)
2112 Это универсальная модульная функция для максимально удобного и быстрого отображения
2113 необходимой вам информации. С ее помощью вводить данные с клавиатуры, осуществлять выбор
2114 из предложенных вариантов, рисовать красивые кнопки, отрисовывать обычный текст,
2115 отрисовывать текстовые поля с возможностью прокрутки, рисовать разделители и прочее.
2116 Любой объект выделяется с помощью клика мыши, после чего функция приступает к работе
2117 с этим объектом.
2118
2119Аргументы функции:
2120 x и y: это числа, обозначающие стартовые координаты левого верхнего угла данного окна.
2121 Вместо цифр вы также можете написать "auto" - и программа автоматически разместит окно
2122 по центру экрана по выбранной координате. Или по обеим координатам, если вам угодно.
2123
2124 width: это ширина окна, которую вы можете задать по собственному желанию. Если некторые
2125 объекты требуют расширения окна, то окно будет автоматически расширено до нужной ширины.
2126 Да, вот такая вот тавтология ;)
2127 background: базовый цвет окна (цвет фона, кому как понятнее).
2128 closeWindowAfter: eсли true, то окно по завершению функции будет выгружено, а на его месте
2129 отрисуются пиксели, которые имелись на экране до выполнения функции. Удобно, если не хочешь
2130 париться с перерисовкой интерфейса.
2131 ... : многоточием тут является перечень объектов, указанных через запятую. Каждый объект
2132 является массивом и имеет собственный формат. Ниже перечислены все возможные типы объектов.
2133
2134 {"Button", {Цвет кнопки1, Цвет текста на кнопке1, Сам текст1}, {Цвет кнопки2, Цвет текста на кнопке2, Сам текст2}, ...}
2135 Это объект для рисования кнопок. Каждая кнопка - это массив, состоящий из трех элементов:
2136 цвета кнопки, цвета текста на кнопке и самого текста. Кнопок может быть неограниченное количество,
2137 однако чем их больше, тем большее требуется разрешение экрана по ширине.
2138 Интерактивный объект.
2139 {"Input", Цвет рамки и текста, Цвет при выделении, Стартовый текст [, Маскировать символом]}
2140 Объект для рисования полей ввода текстовой информации. Удобно для открытия или сохранения файлов,
2141 Опциональный аргумент "Маскировать символом" полезен, если вы делаете поле для ввода пароля.
2142 Никто не увидит ваш текст. В качестве данного аргумента передается символ, например "*".
2143 Интерактивный объект.
2144 {"Selector", Цвет рамки, Цвет при выделении, Выбор 1, Выбор 2, Выбор 3 ...}
2145 Внешне схож с объектом "Input", однако в этом случае вы будете выбирать один из предложенных
2146 вариантов из выпадающего списка. По умолчанию выбран первый вариант.
2147 Интерактивный объект.
2148 {"Select", Цвет рамки, Цвет галочки, Выбор 1, Выбор 2, Выбор 3 ...}
2149 Объект выбора. Отличается от "Selector" тем, что здесь вы выбираете один из вариантов, отмечая
2150 его галочкой. По умолчанию выбран первый вариант.
2151 Интерактивный объект.
2152 {"Slider", Цвет линии слайдера, Цвет пимпочки слайдера, Значения слайдера ОТ, Значения слайдера ДО, Текущее значение [, Текст-подсказка ДО] [, Текст-подсказка ПОСЛЕ]}
2153 Ползунок, позволяющий задавать определенное количество чего-либо в указанном интервале. Имеются два
2154 опциональных аргумента, позволяющих четко понимать, с чем именно мы имеем дело.
2155 К примеру, если аргумент "Текст-подсказка ДО" будет равен "Съедено ", а аргумент "Текст-подсказка ПОСЛЕ"
2156 будет равен " яблок", а значение слайдера будет равно 50, то на экране будет написано "Съедено 50 яблок".
2157 Интерактивный объект.
2158 {"Switch", Активный цвет, Пассивный цвет, Цвет текста, Текст, Состояние}
2159 Переключатель, принимающий два состояния: true или false. Текст - это всего лишь информация, некое
2160 название данного переключателя.
2161 Интерактивный объект.
2162 {"CenterText", Цвет текста, Сам текст}
2163 Отображение текста указанного цвета по центру окна. Чисто для информативных целей.
2164 {"WrappedText", Цвет текста, Текст}
2165 Отображение большого количества текста с автоматическим переносом. Прото режет слова на кусочки,
2166 перенос символический. Чисто для информативных целей.
2167
2168 {"TextField", Высота, Цвет фона, Цвет текста, Цвет скроллбара, Цвет пимпочки скроллбара, Сам текст}
2169
2170 Текстовое поле с возможностью прокрутки. Отличается от "WrappedText"
2171 фиксированной высотой. Чисто для информативных целей.
2172
2173 {"Separator", Цвет разделителя}
2174
2175 Линия-разделитель, помогающая лучше отделять объекты друг от друга. Декоративный объект.
2176
2177 {"EmptyLine"}
2178
2179 Пустое пространство, помогающая лучше отделять объекты друг от друга. Декоративный объект.
2180
2181 Каждый из объектов рисуется по порядку сверху вниз. Каждый объект автоматически
2182 увеличивает высоту окна до необходимого значения. Если объектов будет указано слишком много -
2183 т.е. если окно вылезет за пределы экрана, то программа завершится с ошибкой.
2184 Что возвращает функция:
2185
2186 Возвратом является массив, пронумерованный от 1 до <количества объектов>.
2187 К примеру, 1 индекс данного массива соответствует 1 указанному объекту.
2188 Каждый индекс данного массива несет в себе какие-то данные, которые вы
2189 внесли в объект во время работы функции.
2190 Например, если в 1-ый объект типа "Input" вы ввели фразу "Hello world",
2191 то первый индекс в возвращенном массиве будет равен "Hello world".
2192 Конкретнее это будет вот так: massiv[1] = "Hello world".
2193 Если взаимодействие с объектом невозможно - например, как в случае
2194 с EmptyLine, CenterText, TextField или Separator, то в возвращенном
2195 массиве этот объект указываться не будет.
2196 Готовые примеры использования функции указаны ниже и закомментированы.
2197 Выбирайте нужный и раскомментируйте.
2198]]
2199
2200--Функция-демонстратор, показывающая все возможные объекты в одном окне. Код окна находится выше.
2201--ecs.demoWindow()
2202
2203--Функция, предлагающая сохранить файл в нужном месте в нужном формате.
2204--ecs.universalWindow("auto", "auto", 30, ecs.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Сохранить как"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Путь"}, {"Selector", 0x262626, 0x880000, "PNG", "JPG", "PSD"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK!"}})
2205
2206----------------------------------------------------------------------------------------------------
2207
2208return ecs