mirror of
https://github.com/IgorTimofeev/MineOS.git
synced 2025-12-20 11:09:21 +01:00
2493 lines
96 KiB
Lua
2493 lines
96 KiB
Lua
|
||
-- Адаптивная загрузка необходимых библиотек и компонентов
|
||
local libraries = {
|
||
["component"] = "component",
|
||
["term"] = "term",
|
||
["unicode"] = "unicode",
|
||
["event"] = "event",
|
||
["fs"] = "filesystem",
|
||
["shell"] = "shell",
|
||
["keyboard"] = "keyboard",
|
||
["computer"] = "computer",
|
||
["serialization"] = "serialization",
|
||
--["internet"] = "internet",
|
||
--["image"] = "image",
|
||
}
|
||
|
||
local components = {
|
||
["gpu"] = "gpu",
|
||
}
|
||
|
||
for library in pairs(libraries) do if not _G[library] then _G[library] = require(libraries[library]) end end
|
||
for comp in pairs(components) do if not _G[comp] then _G[comp] = _G.component[components[comp]] end end
|
||
libraries, components = nil, nil
|
||
|
||
local ECSAPI = {}
|
||
|
||
----------------------------------------------------------------------------------------------------
|
||
|
||
ECSAPI.windowColors = {
|
||
background = 0xeeeeee,
|
||
usualText = 0x444444,
|
||
subText = 0x888888,
|
||
tab = 0xaaaaaa,
|
||
title = 0xffffff,
|
||
shadow = 0x444444,
|
||
}
|
||
|
||
ECSAPI.colors = {
|
||
white = 0xffffff,
|
||
orange = 0xF2B233,
|
||
magenta = 0xE57FD8,
|
||
lightBlue = 0x99B2F2,
|
||
yellow = 0xDEDE6C,
|
||
lime = 0x7FCC19,
|
||
pink = 0xF2B2CC,
|
||
gray = 0x4C4C4C,
|
||
lightGray = 0x999999,
|
||
cyan = 0x4C99B2,
|
||
purple = 0xB266E5,
|
||
blue = 0x3366CC,
|
||
brown = 0x7F664C,
|
||
green = 0x57A64E,
|
||
red = 0xCC4C4C,
|
||
black = 0x000000,
|
||
["0"] = 0xffffff,
|
||
["1"] = 0xF2B233,
|
||
["2"] = 0xE57FD8,
|
||
["3"] = 0x99B2F2,
|
||
["4"] = 0xDEDE6C,
|
||
["5"] = 0x7FCC19,
|
||
["6"] = 0xF2B2CC,
|
||
["7"] = 0x4C4C4C,
|
||
["8"] = 0x999999,
|
||
["9"] = 0x4C99B2,
|
||
["a"] = 0xB266E5,
|
||
["b"] = 0x3366CC,
|
||
["c"] = 0x7F664C,
|
||
["d"] = 0x57A64E,
|
||
["e"] = 0xCC4C4C,
|
||
["f"] = 0x000000
|
||
}
|
||
|
||
----------------------------------------------------------------------------------------------------
|
||
|
||
--Установка масштаба монитора
|
||
function ECSAPI.setScale(scale, debug)
|
||
--Базовая коррекция масштаба, чтобы всякие умники не писали своими погаными ручонками, чего не следует
|
||
if scale > 1 then
|
||
scale = 1
|
||
elseif scale < 0.1 then
|
||
scale = 0.1
|
||
end
|
||
|
||
--Просчет монитора в псевдопикселях - забей, даже объяснять не буду, работает как часы
|
||
local function calculateAspect(screens)
|
||
local abc = 12
|
||
|
||
if screens == 2 then
|
||
abc = 28
|
||
elseif screens > 2 then
|
||
abc = 28 + (screens - 2) * 16
|
||
end
|
||
|
||
return abc
|
||
end
|
||
|
||
--Рассчитываем пропорцию монитора в псевдопикселях
|
||
local xScreens, yScreens = component.screen.getAspectRatio()
|
||
local xPixels, yPixels = calculateAspect(xScreens), calculateAspect(yScreens)
|
||
local proportion = xPixels / yPixels
|
||
|
||
--Получаем максимально возможное разрешение данной видеокарты
|
||
local xMax, yMax = gpu.maxResolution()
|
||
|
||
--Получаем теоретическое максимальное разрешение монитора с учетом его пропорции, но без учета лимита видеокарты
|
||
local newWidth, newHeight
|
||
if proportion >= 1 then
|
||
newWidth = math.floor(xMax)
|
||
newHeight = math.floor(newWidth / proportion / 2)
|
||
else
|
||
newHeight = math.floor(yMax)
|
||
newWidth = math.floor(newHeight * proportion * 2)
|
||
end
|
||
|
||
--Получаем оптимальное разрешение для данного монитора с поддержкой видеокарты
|
||
local optimalNewWidth, optimalNewHeight = newWidth, newHeight
|
||
|
||
if optimalNewWidth > xMax then
|
||
local difference = optimalNewWidth - xMax
|
||
optimalNewWidth = xMax
|
||
optimalNewHeight = optimalNewHeight - math.ceil(difference / 2 )
|
||
end
|
||
|
||
if optimalNewHeight > yMax then
|
||
local difference = optimalNewHeight - yMax
|
||
optimalNewHeight = yMax
|
||
--optimalNewWidth = optimalNewWidth - difference * 2 - math.ceil(difference / 2)
|
||
optimalNewWidth = optimalNewWidth - difference * 2
|
||
end
|
||
|
||
--Корректируем идеальное разрешение по заданному масштабу
|
||
local finalNewWidth, finalNewHeight = math.floor(optimalNewWidth * scale), math.floor(optimalNewHeight * scale)
|
||
|
||
--Выводим инфу, если нужно
|
||
if debug then
|
||
print(" ")
|
||
print("Максимальное разрешение: "..xMax.."x"..yMax)
|
||
print("Пропорция монитора: "..xPixels.."x"..yPixels)
|
||
print("Коэффициент пропорции: "..proportion)
|
||
print(" ")
|
||
print("Теоретическое разрешение: "..newWidth.."x"..newHeight)
|
||
print("Оптимизированное разрешение: "..optimalNewWidth.."x"..optimalNewHeight)
|
||
print(" ")
|
||
print("Новое разрешение: "..finalNewWidth.."x"..finalNewHeight)
|
||
print(" ")
|
||
end
|
||
|
||
--Устанавливаем выбранное разрешение
|
||
gpu.setResolution(finalNewWidth, finalNewHeight)
|
||
end
|
||
|
||
function ECSAPI.rebindGPU(address)
|
||
gpu.bind(address)
|
||
end
|
||
|
||
--Получаем всю инфу об оперативку в килобайтах
|
||
function ECSAPI.getInfoAboutRAM()
|
||
local free = math.floor(computer.freeMemory() / 1024)
|
||
local total = math.floor(computer.totalMemory() / 1024)
|
||
local used = total - free
|
||
|
||
return free, total, used
|
||
end
|
||
|
||
--Получить информацию о жестких дисках
|
||
function ECSAPI.getHDDs()
|
||
local candidates = {}
|
||
for address in component.list("filesystem") do
|
||
local proxy = component.proxy(address)
|
||
if proxy.address ~= computer.tmpAddress() and proxy.getLabel() ~= "internet" then
|
||
local isFloppy, spaceTotal = false, math.floor(proxy.spaceTotal() / 1024)
|
||
if spaceTotal < 600 then isFloppy = true end
|
||
table.insert(candidates, {
|
||
["spaceTotal"] = spaceTotal,
|
||
["spaceUsed"] = math.floor(proxy.spaceUsed() / 1024),
|
||
["label"] = proxy.getLabel(),
|
||
["address"] = proxy.address,
|
||
["isReadOnly"] = proxy.isReadOnly(),
|
||
["isFloppy"] = isFloppy,
|
||
})
|
||
end
|
||
end
|
||
return candidates
|
||
end
|
||
|
||
--Форматировать диск
|
||
function ECSAPI.formatHDD(address)
|
||
local proxy = component.proxy(address)
|
||
local list = proxy.list("")
|
||
ECSAPI.info("auto", "auto", "", "Formatting disk...")
|
||
for _, file in pairs(list) do
|
||
if type(file) == "string" then
|
||
if not proxy.isReadOnly(file) then proxy.remove(file) end
|
||
end
|
||
end
|
||
list = nil
|
||
end
|
||
|
||
--Установить имя жесткого диска
|
||
function ECSAPI.setHDDLabel(address, label)
|
||
local proxy = component.proxy(address)
|
||
proxy.setLabel(label or "Untitled")
|
||
end
|
||
|
||
--Найти монтированный путь конкретного адреса диска
|
||
function ECSAPI.findMount(address)
|
||
for fs1, path in fs.mounts() do
|
||
if fs1.address == component.get(address) then
|
||
return path
|
||
end
|
||
end
|
||
end
|
||
|
||
function ECSAPI.getArraySize(array)
|
||
local size = 0
|
||
for key in pairs(array) do
|
||
size = size + 1
|
||
end
|
||
return size
|
||
end
|
||
|
||
--Скопировать файлы с одного диска на другой с заменой
|
||
function ECSAPI.duplicateFileSystem(fromAddress, toAddress)
|
||
local source, destination = ECSAPI.findMount(fromAddress), ECSAPI.findMount(toAddress)
|
||
ECSAPI.info("auto", "auto", "", "Copying file system...")
|
||
shell.execute("bin/cp -rx "..source.."* "..destination)
|
||
end
|
||
|
||
--Загрузка файла с инета
|
||
function ECSAPI.getFileFromUrl(url, path)
|
||
if not _G.internet then _G.internet = require("internet") end
|
||
|
||
local result, response = pcall(internet.request, url)
|
||
if not result then
|
||
ECSAPI.error("Could not connect to to URL address \"" .. url .. "\"")
|
||
return
|
||
end
|
||
|
||
fs.remove(path)
|
||
fs.makeDirectory(fs.path(path))
|
||
local file = io.open(path, "w")
|
||
|
||
for chunk in response do
|
||
file:write(chunk)
|
||
end
|
||
|
||
file:close()
|
||
end
|
||
|
||
--Загрузка файла с пастебина
|
||
function ECSAPI.getFromPastebin(paste, path)
|
||
local url = "http://pastebin.com/raw.php?i=" .. paste
|
||
ECSAPI.getFileFromUrl(url, path)
|
||
end
|
||
|
||
--Загрузка файла с гитхаба
|
||
function ECSAPI.getFromGitHub(url, path)
|
||
url = "https://raw.githubusercontent.com/" .. url
|
||
ECSAPI.getFileFromUrl(url, path)
|
||
end
|
||
|
||
--Загрузить ОС-приложение
|
||
function ECSAPI.getOSApplication(application, downloadWallpapers)
|
||
downloadWallpapers = downloadWallpapers or true
|
||
--Если это приложение
|
||
if application.type == "Application" then
|
||
--Удаляем приложение, если оно уже существовало и создаем все нужные папочки
|
||
fs.remove(application.name .. ".app")
|
||
fs.makeDirectory(application.name .. ".app/Resources")
|
||
|
||
--Загружаем основной исполняемый файл и иконку
|
||
ECSAPI.getFromGitHub(application.url, application.name .. ".app/" .. fs.name(application.name .. ".lua"))
|
||
ECSAPI.getFromGitHub(application.icon, application.name .. ".app/Resources/Icon.pic")
|
||
|
||
--Если есть ресурсы, то загружаем ресурсы
|
||
if application.resources then
|
||
for i = 1, #application.resources do
|
||
ECSAPI.getFromGitHub(application.resources[i].url, application.name .. ".app/Resources/" .. application.resources[i].name)
|
||
end
|
||
end
|
||
|
||
--Если есть файл "о программе", то грузим и его
|
||
if application.about then
|
||
ECSAPI.getFromGitHub(application.about, application.name .. ".app/Resources/About.txt")
|
||
end
|
||
|
||
--Если имеется режим создания ярлыка, то создаем его
|
||
if application.createShortcut then
|
||
local desktopPath = "MineOS/Desktop/"
|
||
local dockPath = "MineOS/System/OS/Dock/"
|
||
|
||
if application.createShortcut == "dock" then
|
||
ECSAPI.createShortCut(dockPath .. fs.name(application.name) .. ".lnk", application.name .. ".app")
|
||
else
|
||
ECSAPI.createShortCut(desktopPath .. fs.name(application.name) .. ".lnk", application.name .. ".app")
|
||
end
|
||
end
|
||
|
||
--Если тип = другой, чужой, а мб и свой пастебин
|
||
elseif application.type == "Pastebin" then
|
||
ECSAPI.getFromPastebin(application.url, application.name)
|
||
|
||
--Если обои
|
||
elseif application.type == "Wallpaper" then
|
||
if downloadWallpapers then
|
||
ECSAPI.getFromGitHub(application.url, application.name)
|
||
end
|
||
|
||
--Если просто какой-то скрипт
|
||
elseif application.type == "Script" or application.type == "Library" then
|
||
ECSAPI.getFromGitHub(application.url, application.name)
|
||
|
||
--А если ваще какая-то абстрактная хуйня, либо ссылка на веб, то загружаем по УРЛ-ке
|
||
else
|
||
ECSAPI.getFileFromUrl(application.url, application.name)
|
||
end
|
||
end
|
||
|
||
--Получить список приложений, которые требуется обновить
|
||
function ECSAPI.getAppsToUpdate(debug)
|
||
--Задаем стартовые пути
|
||
local pathToApplicationsFile = "MineOS/System/OS/Applications.txt"
|
||
local pathToSecondApplicationsFile = "MineOS/System/OS/Applications2.txt"
|
||
--Путь к файл-листу на пастебине
|
||
local paste = "3j2x4dDn"
|
||
--Выводим инфу
|
||
local oldPixels
|
||
if debug then oldPixels = ECSAPI.info("auto", "auto", " ", "Checking for updates...") end
|
||
--Получаем свеженький файл
|
||
ECSAPI.getFromPastebin(paste, pathToSecondApplicationsFile)
|
||
--Читаем оба файла
|
||
local file = io.open(pathToApplicationsFile, "r")
|
||
local applications = serialization.unserialize(file:read("*a"))
|
||
file:close()
|
||
--И второй
|
||
file = io.open(pathToSecondApplicationsFile, "r")
|
||
local applications2 = serialization.unserialize(file:read("*a"))
|
||
file:close()
|
||
|
||
local countOfUpdates = 0
|
||
|
||
--Просматриваем свеженький файлик и анализируем, че в нем нового, все старое удаляем
|
||
local i = 1
|
||
while true do
|
||
--Разрыв цикла
|
||
if i > #applications2 then break end
|
||
--Новая версия файла
|
||
local newVersion, oldVersion = applications2[i].version, 0
|
||
--Получаем старую версию этого файла
|
||
for j = 1, #applications do
|
||
if applications2[i].name == applications[j].name then
|
||
oldVersion = applications[j].version or 0
|
||
break
|
||
end
|
||
end
|
||
--Если новая версия новее, чем старая, то добавить в массив то, что нужно обновить
|
||
if newVersion > oldVersion then
|
||
applications2[i].needToUpdate = true
|
||
countOfUpdates = countOfUpdates + 1
|
||
end
|
||
|
||
i = i + 1
|
||
end
|
||
--Если чет рисовалось, то стереть на хер
|
||
if oldPixels then ECSAPI.drawOldPixels(oldPixels) end
|
||
--Возвращаем массив с тем, че нужно обновить и просто старый аппликашнс на всякий случай
|
||
return applications2, countOfUpdates
|
||
end
|
||
|
||
--Сделать строку пригодной для отображения в ОпенКомпах
|
||
--Заменяет табсы на пробелы и виндовый возврат каретки на человеческий UNIX-овский
|
||
function ECSAPI.stringOptimize(sto4ka, indentatonWidth)
|
||
sto4ka = string.gsub(sto4ka, "\r\n", "\n")
|
||
sto4ka = string.gsub(sto4ka, " ", string.rep(" ", indentatonWidth or 2))
|
||
return stro4ka
|
||
end
|
||
|
||
--ИЗ ДЕСЯТИЧНОЙ В ШЕСТНАДЦАТИРИЧНУЮ
|
||
function ECSAPI.decToBase(IN,BASE)
|
||
local hexCode = "0123456789ABCDEFGHIJKLMNOPQRSTUVW"
|
||
OUT = ""
|
||
local ostatok = 0
|
||
while IN>0 do
|
||
ostatok = math.fmod(IN,BASE) + 1
|
||
IN = math.floor(IN/BASE)
|
||
OUT = string.sub(hexCode,ostatok,ostatok)..OUT
|
||
end
|
||
if #OUT == 1 then OUT = "0"..OUT end
|
||
if OUT == "" then OUT = "00" end
|
||
return OUT
|
||
end
|
||
|
||
--Правильное конвертирование HEX-переменной в строковую
|
||
function ECSAPI.HEXtoString(color, bitCount, withNull)
|
||
local stro4ka = string.format("%X",color)
|
||
local sStro4ka = unicode.len(stro4ka)
|
||
if sStro4ka < bitCount then
|
||
stro4ka = string.rep("0", bitCount - sStro4ka) .. stro4ka
|
||
end
|
||
sStro4ka = nil
|
||
if withNull then return "0x"..stro4ka else return stro4ka end
|
||
end
|
||
|
||
--КЛИКНУЛИ ЛИ В ЗОНУ
|
||
function ECSAPI.clickedAtArea(x,y,sx,sy,ex,ey)
|
||
if (x >= sx) and (x <= ex) and (y >= sy) and (y <= ey) then return true end
|
||
return false
|
||
end
|
||
|
||
--Заливка всего экрана указанным цветом
|
||
function ECSAPI.clearScreen(color)
|
||
if color then gpu.setBackground(color) end
|
||
term.clear()
|
||
end
|
||
|
||
--Установка пикселя нужного цвета
|
||
function ECSAPI.setPixel(x,y,color)
|
||
gpu.setBackground(color)
|
||
gpu.set(x,y," ")
|
||
end
|
||
|
||
--Простая установка цветов в одну строку, ибо я ленивый
|
||
function ECSAPI.setColor(background, foreground)
|
||
gpu.setBackground(background)
|
||
gpu.setForeground(foreground)
|
||
end
|
||
|
||
--Цветной текст
|
||
function ECSAPI.colorText(x,y,textColor,text)
|
||
gpu.setForeground(textColor)
|
||
gpu.set(x,y,text)
|
||
end
|
||
|
||
--Цветной текст с жопкой!
|
||
function ECSAPI.colorTextWithBack(x,y,textColor,backColor,text)
|
||
gpu.setForeground(textColor)
|
||
gpu.setBackground(backColor)
|
||
gpu.set(x,y,text)
|
||
end
|
||
|
||
--Инверсия цвета
|
||
function ECSAPI.invertColor(color)
|
||
return 0xffffff - color
|
||
end
|
||
|
||
--Адаптивный текст, подстраивающийся под фон
|
||
function ECSAPI.adaptiveText(x,y,text,textColor)
|
||
gpu.setForeground(textColor)
|
||
x = x - 1
|
||
for i=1,unicode.len(text) do
|
||
local info = {gpu.get(x+i,y)}
|
||
gpu.setBackground(info[3])
|
||
gpu.set(x+i,y,unicode.sub(text,i,i))
|
||
end
|
||
end
|
||
|
||
--Костыльная замена обычному string.find()
|
||
--Работает медленнее, но хотя бы поддерживает юникод
|
||
function unicode.find(str, pattern, init, plain)
|
||
if init then
|
||
if init < 0 then
|
||
init = -#unicode.sub(str,init)
|
||
elseif init > 0 then
|
||
init = #unicode.sub(str,1,init-1)+1
|
||
end
|
||
end
|
||
|
||
a, b = string.find(str, pattern, init, plain)
|
||
|
||
if a then
|
||
local ap,bp = str:sub(1,a-1), str:sub(a,b)
|
||
a = unicode.len(ap)+1
|
||
b = a + unicode.len(bp)-1
|
||
return a,b
|
||
else
|
||
return a
|
||
end
|
||
end
|
||
|
||
--Умный текст по аналогии с майнчатовским. Ставишь символ параграфа, указываешь хуйню - и хуякс! Работает!
|
||
function ECSAPI.smartText(x, y, text)
|
||
local sText = unicode.len(text)
|
||
local specialSymbol = "§"
|
||
--Разбираем по кусочкам строку и получаем цвета
|
||
local massiv = {}
|
||
local iterator = 1
|
||
local currentColor = gpu.getForeground()
|
||
while iterator <= sText do
|
||
local symbol = unicode.sub(text, iterator, iterator)
|
||
if symbol == specialSymbol then
|
||
currentColor = ECSAPI.colors[unicode.sub(text, iterator + 1, iterator + 1) or "f"]
|
||
iterator = iterator + 1
|
||
else
|
||
table.insert(massiv, {symbol, currentColor})
|
||
end
|
||
symbol = nil
|
||
iterator = iterator + 1
|
||
end
|
||
x = x - 1
|
||
for i = 1, #massiv do
|
||
if currentColor ~= massiv[i][2] then currentColor = massiv[i][2]; gpu.setForeground(massiv[i][2]) end
|
||
gpu.set(x + i, y, massiv[i][1])
|
||
end
|
||
end
|
||
|
||
--Аналог умного текста, но использующий HEX-цвета для кодировки
|
||
function ECSAPI.formattedText(x, y, text, limit)
|
||
--Ограничение длины строки
|
||
limit = limit or math.huge
|
||
--Стартовая позиция курсора для отрисовки
|
||
local xPos = x
|
||
--Создаем массив символов данной строки
|
||
local symbols = {}
|
||
for i = 1, unicode.len(text) do table.insert(symbols, unicode.sub(text, i, i)) end
|
||
--Перебираем все символы строки, пока не переберем все или не достигнем указанного лимита
|
||
local i = 1
|
||
while i <= #symbols and i <= limit do
|
||
--Если находим символ параграфа, то
|
||
if symbols[i] == "§" then
|
||
--Меняем цвет текста на указанный
|
||
gpu.setForeground(tonumber("0x" .. symbols[i+1] .. symbols[i+2] .. symbols[i+3] .. symbols[i+4] .. symbols[i+5] .. symbols[i+6]))
|
||
--Увеличиваем лимит на 7, т.к.
|
||
limit = limit + 7
|
||
--Сдвигаем итератор цикла на 7
|
||
i = i + 7
|
||
end
|
||
--Рисуем символ на нужной позиции
|
||
gpu.set(xPos, y, symbols[i])
|
||
--Увеличиваем позицию курсора и итератор на 1
|
||
xPos = xPos + 1
|
||
i = i + 1
|
||
end
|
||
end
|
||
|
||
--Инвертированный текст на основе цвета фона
|
||
function ECSAPI.invertedText(x,y,symbol)
|
||
local info = {gpu.get(x,y)}
|
||
ECSAPI.adaptiveText(x,y,symbol,ECSAPI.invertColor(info[3]))
|
||
end
|
||
|
||
--Адаптивное округление числа
|
||
function ECSAPI.adaptiveRound(chislo)
|
||
local celaya,drobnaya = math.modf(chislo)
|
||
if drobnaya >= 0.5 then
|
||
return (celaya + 1)
|
||
else
|
||
return celaya
|
||
end
|
||
end
|
||
|
||
--Округление до опред. кол-ва знаков после запятой
|
||
function ECSAPI.round(num, idp)
|
||
local mult = 10^(idp or 0)
|
||
return math.floor(num * mult + 0.5) / mult
|
||
end
|
||
|
||
--Обычный квадрат указанного цвета
|
||
function ECSAPI.square(x,y,width,height,color)
|
||
gpu.setBackground(color)
|
||
gpu.fill(x,y,width,height," ")
|
||
end
|
||
|
||
--Юникодовская рамка
|
||
function ECSAPI.border(x, y, width, height, back, fore)
|
||
local stringUp = "┌"..string.rep("─", width - 2).."┐"
|
||
local stringDown = "└"..string.rep("─", width - 2).."┘"
|
||
gpu.setForeground(fore)
|
||
gpu.setBackground(back)
|
||
gpu.set(x, y, stringUp)
|
||
gpu.set(x, y + height - 1, stringDown)
|
||
|
||
local yPos = 1
|
||
for i = 1, (height - 2) do
|
||
gpu.set(x, y + yPos, "│")
|
||
gpu.set(x + width - 1, y + yPos, "│")
|
||
yPos = yPos + 1
|
||
end
|
||
end
|
||
|
||
--Кнопка в виде текста в рамке
|
||
function ECSAPI.drawFramedButton(x, y, width, height, text, color)
|
||
ECSAPI.border(x, y, width, height, gpu.getBackground(), color)
|
||
gpu.fill(x + 1, y + 1, width - 2, height - 2, " ")
|
||
x = x + math.floor(width / 2 - unicode.len(text) / 2)
|
||
y = y + math.floor(width / 2 - 1)
|
||
gpu.set(x, y, text)
|
||
end
|
||
|
||
--Юникодовский разделитель
|
||
function ECSAPI.separator(x, y, width, back, fore)
|
||
ECSAPI.colorTextWithBack(x, y, fore, back, string.rep("─", width))
|
||
end
|
||
|
||
--Автоматическое центрирование текста по указанной координате (x, y, xy)
|
||
function ECSAPI.centerText(mode,coord,text)
|
||
local dlina = unicode.len(text)
|
||
local xSize,ySize = gpu.getResolution()
|
||
|
||
if mode == "x" then
|
||
gpu.set(math.floor(xSize/2-dlina/2),coord,text)
|
||
elseif mode == "y" then
|
||
gpu.set(coord,math.floor(ySize/2),text)
|
||
else
|
||
gpu.set(math.floor(xSize/2-dlina/2),math.floor(ySize/2),text)
|
||
end
|
||
end
|
||
|
||
--Отрисовка "изображения" по указанному массиву
|
||
function ECSAPI.drawCustomImage(x,y,pixels)
|
||
x = x - 1
|
||
y = y - 1
|
||
local pixelsWidth = #pixels[1]
|
||
local pixelsHeight = #pixels
|
||
local xEnd = x + pixelsWidth
|
||
local yEnd = y + pixelsHeight
|
||
|
||
for i=1,pixelsHeight do
|
||
for j=1,pixelsWidth do
|
||
if pixels[i][j][3] ~= "#" then
|
||
if gpu.getBackground() ~= pixels[i][j][1] then gpu.setBackground(pixels[i][j][1]) end
|
||
if gpu.getForeground() ~= pixels[i][j][2] then gpu.setForeground(pixels[i][j][2]) end
|
||
gpu.set(x+j,y+i,pixels[i][j][3])
|
||
end
|
||
end
|
||
end
|
||
|
||
return (x+1),(y+1),xEnd,yEnd
|
||
end
|
||
|
||
--Корректировка стартовых координат. Core-функция для всех моих программ
|
||
function ECSAPI.correctStartCoords(xStart,yStart,xWindowSize,yWindowSize)
|
||
local xSize,ySize = gpu.getResolution()
|
||
if xStart == "auto" then
|
||
xStart = math.floor(xSize/2 - xWindowSize/2)
|
||
end
|
||
if yStart == "auto" then
|
||
yStart = math.ceil(ySize/2 - yWindowSize/2)
|
||
end
|
||
return xStart,yStart
|
||
end
|
||
|
||
--Запомнить область пикселей и возвратить ее в виде массива
|
||
function ECSAPI.rememberOldPixels(x, y, x2, y2)
|
||
local newPNGMassiv = { ["backgrounds"] = {} }
|
||
local xSize, ySize = gpu.getResolution()
|
||
newPNGMassiv.x, newPNGMassiv.y = x, y
|
||
|
||
--Перебираем весь массив стандартного PNG-вида по высоте
|
||
local xCounter, yCounter = 1, 1
|
||
for j = y, y2 do
|
||
xCounter = 1
|
||
for i = x, x2 do
|
||
|
||
if (i > xSize or i < 0) or (j > ySize or j < 0) then
|
||
error("Can't remember pixel, because it's located behind the screen: x("..i.."), y("..j..") out of xSize("..xSize.."), ySize("..ySize..")\n")
|
||
end
|
||
|
||
local symbol, fore, back = gpu.get(i, j)
|
||
|
||
newPNGMassiv["backgrounds"][back] = newPNGMassiv["backgrounds"][back] or {}
|
||
newPNGMassiv["backgrounds"][back][fore] = newPNGMassiv["backgrounds"][back][fore] or {}
|
||
|
||
table.insert(newPNGMassiv["backgrounds"][back][fore], {xCounter, yCounter, symbol} )
|
||
|
||
xCounter = xCounter + 1
|
||
back, fore, symbol = nil, nil, nil
|
||
end
|
||
|
||
yCounter = yCounter + 1
|
||
end
|
||
|
||
xSize, ySize = nil, nil
|
||
return newPNGMassiv
|
||
end
|
||
|
||
--Нарисовать запомненные ранее пиксели из массива
|
||
function ECSAPI.drawOldPixels(massivSudaPihay)
|
||
--Перебираем массив с фонами
|
||
for back, backValue in pairs(massivSudaPihay["backgrounds"]) do
|
||
gpu.setBackground(back)
|
||
for fore, foreValue in pairs(massivSudaPihay["backgrounds"][back]) do
|
||
gpu.setForeground(fore)
|
||
for pixel = 1, #massivSudaPihay["backgrounds"][back][fore] do
|
||
if massivSudaPihay["backgrounds"][back][fore][pixel][3] ~= transparentSymbol then
|
||
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])
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
--Ограничение длины строки. Маст-хев функция.
|
||
function ECSAPI.stringLimit(mode, text, size, noDots)
|
||
if unicode.len(text) <= size then return text end
|
||
local length = unicode.len(text)
|
||
if mode == "start" then
|
||
if noDots then
|
||
return unicode.sub(text, length - size + 1, -1)
|
||
else
|
||
return "…" .. unicode.sub(text, length - size + 2, -1)
|
||
end
|
||
else
|
||
if noDots then
|
||
return unicode.sub(text, 1, size)
|
||
else
|
||
return unicode.sub(text, 1, size - 1) .. "…"
|
||
end
|
||
end
|
||
end
|
||
|
||
--Получить текущее реальное время компьютера, хостящего сервер майна
|
||
function ECSAPI.getHostTime(timezone)
|
||
timezone = timezone or 2
|
||
--Создаем файл с записанной в него парашей
|
||
local file = io.open("HostTime.tmp", "w")
|
||
file:write("")
|
||
file:close()
|
||
--Коррекция времени на основе часового пояса
|
||
local timeCorrection = timezone * 3600
|
||
--Получаем дату изменения файла в юникс-виде
|
||
local lastModified = tonumber(string.sub(fs.lastModified("HostTime.tmp"), 1, -4)) + timeCorrection
|
||
--Удаляем файл, ибо на хуй он нам не нужен
|
||
fs.remove("HostTime.tmp")
|
||
--Конвертируем юникс-время в норм время
|
||
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)
|
||
--Возвращаем все
|
||
return tonumber(day), tonumber(month), tonumber(year), tonumber(hour), tonumber(minute), tonumber(second)
|
||
end
|
||
|
||
--Получить спискок файлов из конкретной директории, костыль
|
||
function ECSAPI.getFileList(path)
|
||
local list = fs.list(path)
|
||
local massiv = {}
|
||
for file in list do
|
||
--if string.find(file, "%/$") then file = unicode.sub(file, 1, -2) end
|
||
table.insert(massiv, file)
|
||
end
|
||
list = nil
|
||
return massiv
|
||
end
|
||
|
||
--Получить файловое древо. Сильно нагружает систему, только для дебага!
|
||
function ECSAPI.getFileTree(path)
|
||
local massiv = {}
|
||
local list = ECSAPI.getFileList(path)
|
||
for key, file in pairs(list) do
|
||
if fs.isDirectory(path.."/"..file) then
|
||
table.insert(massiv, getFileTree(path.."/"..file))
|
||
else
|
||
table.insert(massiv, file)
|
||
end
|
||
end
|
||
list = nil
|
||
|
||
return massiv
|
||
end
|
||
|
||
--Поиск по файловой системе
|
||
function ECSAPI.find(path, cheBudemIskat)
|
||
--Массив, в котором будут находиться все найденные соответствия
|
||
local massivNaydennogoGovna = {}
|
||
--Костыль, но удобный
|
||
local function dofind(path, cheBudemIskat)
|
||
--Получаем список файлов в директории
|
||
local list = ECSAPI.getFileList(path)
|
||
--Перебираем все элементы файл листа
|
||
for key, file in pairs(list) do
|
||
--Путь к файлу
|
||
local pathToFile = path..file
|
||
--Если нашло совпадение в имени файла, то выдает путь к этому файлу
|
||
if string.find(unicode.lower(file), unicode.lower(cheBudemIskat)) then
|
||
table.insert(massivNaydennogoGovna, pathToFile)
|
||
end
|
||
--Анализ, что делать дальше
|
||
if fs.isDirectory(pathToFile) then
|
||
dofind(pathToFile, cheBudemIskat)
|
||
end
|
||
--Очищаем оперативку
|
||
pathToFile = nil
|
||
end
|
||
--Очищаем оперативку
|
||
list = nil
|
||
end
|
||
--Выполняем функцию
|
||
dofind(path, cheBudemIskat)
|
||
--Возвращаем, че нашло
|
||
return massivNaydennogoGovna
|
||
end
|
||
|
||
--Получение формата файла
|
||
function ECSAPI.getFileFormat(path)
|
||
local name = fs.name(path)
|
||
local starting, ending = string.find(name, "(.)%.[%d%w]*$")
|
||
if starting == nil then
|
||
return nil
|
||
else
|
||
return unicode.sub(name,starting + 1, -1)
|
||
end
|
||
name, starting, ending = nil, nil, nil
|
||
end
|
||
|
||
--Проверить, скрытый ли файл (.пидор, .хуй = true; пидор, хуй = false)
|
||
function ECSAPI.isFileHidden(path)
|
||
local name = fs.name(path)
|
||
local starting, ending = string.find(name, "^%.(.*)$")
|
||
if starting == nil then
|
||
return false
|
||
else
|
||
return true
|
||
end
|
||
name, starting, ending = nil, nil, nil
|
||
end
|
||
|
||
--Скрыть формат файла
|
||
function ECSAPI.hideFileFormat(path)
|
||
local name = fs.name(path)
|
||
local fileFormat = ECSAPI.getFileFormat(name)
|
||
if fileFormat == nil then
|
||
return name
|
||
else
|
||
return unicode.sub(name, 1, unicode.len(name) - unicode.len(fileFormat))
|
||
end
|
||
end
|
||
|
||
--Ожидание клика либо нажатия какой-либо клавиши
|
||
function ECSAPI.waitForTouchOrClick()
|
||
while true do
|
||
local e = { event.pull() }
|
||
if e[1] == "key_down" or e[1] == "touch" then break end
|
||
end
|
||
end
|
||
|
||
--То же самое, но в сокращенном варианте
|
||
function ECSAPI.wait()
|
||
ECSAPI.waitForTouchOrClick()
|
||
end
|
||
|
||
--Нарисовать кнопочки закрытия окна
|
||
function ECSAPI.drawCloses(x, y, active)
|
||
local symbol = "⮾"
|
||
ECSAPI.colorText(x, y , (active == 1 and ECSAPI.colors.blue) or 0xCC4C4C, symbol)
|
||
ECSAPI.colorText(x + 2, y , (active == 2 and ECSAPI.colors.blue) or 0xDEDE6C, symbol)
|
||
ECSAPI.colorText(x + 4, y , (active == 3 and ECSAPI.colors.blue) or 0x57A64E, symbol)
|
||
end
|
||
|
||
--Нарисовать верхнюю оконную панель с выбором объектов
|
||
function ECSAPI.drawTopBar(x, y, width, selectedObject, background, foreground, ...)
|
||
local objects = { ... }
|
||
ECSAPI.square(x, y, width, 3, background)
|
||
local widthOfObjects = 0
|
||
local spaceBetween = 2
|
||
for i = 1, #objects do
|
||
widthOfObjects = widthOfObjects + unicode.len(objects[i][1]) + spaceBetween
|
||
end
|
||
local xPos = x + math.floor(width / 2 - widthOfObjects / 2)
|
||
for i = 1, #objects do
|
||
if i == selectedObject then
|
||
ECSAPI.square(xPos, y, unicode.len(objects[i][1]) + spaceBetween, 3, ECSAPI.colors.blue)
|
||
gpu.setForeground(0xffffff)
|
||
else
|
||
gpu.setBackground(background)
|
||
gpu.setForeground(foreground)
|
||
end
|
||
gpu.set(xPos + spaceBetween / 2, y + 2, objects[i][1])
|
||
gpu.set(xPos + math.ceil(unicode.len(objects[i][1]) / 2), y + 1, objects[i][2])
|
||
|
||
xPos = xPos + unicode.len(objects[i][1]) + spaceBetween
|
||
end
|
||
end
|
||
|
||
--Нарисовать топ-меню, горизонтальная полоска такая с текстами
|
||
function ECSAPI.drawTopMenu(x, y, width, color, selectedObject, ...)
|
||
local objects = { ... }
|
||
local objectsToReturn = {}
|
||
local xPos = x + 2
|
||
local spaceBetween = 2
|
||
ECSAPI.square(x, y, width, 1, color)
|
||
for i = 1, #objects do
|
||
if i == selectedObject then
|
||
ECSAPI.square(xPos - 1, y, unicode.len(objects[i][1]) + spaceBetween, 1, ECSAPI.colors.blue)
|
||
gpu.setForeground(0xffffff)
|
||
gpu.set(xPos, y, objects[i][1])
|
||
gpu.setForeground(objects[i][2])
|
||
gpu.setBackground(color)
|
||
else
|
||
if gpu.getForeground() ~= objects[i][2] then gpu.setForeground(objects[i][2]) end
|
||
gpu.set(xPos, y, objects[i][1])
|
||
end
|
||
objectsToReturn[objects[i][1]] = { xPos, y, xPos + unicode.len(objects[i][1]) - 1, y, i }
|
||
xPos = xPos + unicode.len(objects[i][1]) + spaceBetween
|
||
end
|
||
return objectsToReturn
|
||
end
|
||
|
||
--Функция отрисовки кнопки указанной ширины
|
||
function ECSAPI.drawButton(x,y,width,height,text,backColor,textColor)
|
||
x,y = ECSAPI.correctStartCoords(x,y,width,height)
|
||
|
||
local textPosX = math.floor(x + width / 2 - unicode.len(text) / 2)
|
||
local textPosY = math.floor(y + height / 2)
|
||
ECSAPI.square(x,y,width,height,backColor)
|
||
ECSAPI.colorText(textPosX,textPosY,textColor,text)
|
||
|
||
return x, y, (x + width - 1), (y + height - 1)
|
||
end
|
||
|
||
--Отрисовка кнопки с указанными отступами от текста
|
||
function ECSAPI.drawAdaptiveButton(x,y,offsetX,offsetY,text,backColor,textColor)
|
||
local length = unicode.len(text)
|
||
local width = offsetX*2 + length
|
||
local height = offsetY*2 + 1
|
||
|
||
x,y = ECSAPI.correctStartCoords(x,y,width,height)
|
||
|
||
ECSAPI.square(x,y,width,height,backColor)
|
||
ECSAPI.colorText(x+offsetX,y+offsetY,textColor,text)
|
||
|
||
return x,y,(x+width-1),(y+height-1)
|
||
end
|
||
|
||
--Отрисовка оконной "тени"
|
||
function ECSAPI.windowShadow(x,y,width,height)
|
||
gpu.setBackground(ECSAPI.windowColors.shadow)
|
||
gpu.fill(x+width,y+1,2,height," ")
|
||
gpu.fill(x+1,y+height,width,1," ")
|
||
end
|
||
|
||
--Просто белое окошко с тенью
|
||
function ECSAPI.blankWindow(x,y,width,height)
|
||
local oldPixels = ECSAPI.rememberOldPixels(x,y,x+width+1,y+height)
|
||
|
||
ECSAPI.square(x,y,width,height,ECSAPI.windowColors.background)
|
||
|
||
ECSAPI.windowShadow(x,y,width,height)
|
||
|
||
return oldPixels
|
||
end
|
||
|
||
--Белое окошко, но уже с титлом вверху!
|
||
function ECSAPI.emptyWindow(x,y,width,height,title)
|
||
|
||
local oldPixels = ECSAPI.rememberOldPixels(x,y,x+width+1,y+height)
|
||
|
||
--ОКНО
|
||
gpu.setBackground(ECSAPI.windowColors.background)
|
||
gpu.fill(x,y+1,width,height-1," ")
|
||
|
||
--ТАБ СВЕРХУ
|
||
gpu.setBackground(ECSAPI.windowColors.tab)
|
||
gpu.fill(x,y,width,1," ")
|
||
|
||
--ТИТЛ
|
||
gpu.setForeground(ECSAPI.windowColors.title)
|
||
local textPosX = x + math.floor(width/2-unicode.len(title)/2) -1
|
||
gpu.set(textPosX,y,title)
|
||
|
||
--ТЕНЬ
|
||
ECSAPI.windowShadow(x,y,width,height)
|
||
|
||
return oldPixels
|
||
|
||
end
|
||
|
||
--Функция по переносу слов на новую строку в зависимости от ограничения по ширине
|
||
function ECSAPI.stringWrap(strings, limit)
|
||
-- local massiv = {}
|
||
local firstSlice, secondSlice
|
||
--Перебираем все указанные строки
|
||
local i = 1
|
||
while i <= #strings do
|
||
|
||
if unicode.len(strings[i]) > limit then
|
||
firstSlice = unicode.sub(strings[i], 1, limit)
|
||
secondSlice = unicode.sub(strings[i], limit + 1, -1)
|
||
|
||
strings[i] = firstSlice
|
||
table.insert(strings, i + 1, secondSlice)
|
||
end
|
||
|
||
i = i + 1
|
||
|
||
-- --Создаем массив слов данной строки
|
||
-- local words = {}
|
||
-- for match in string.gmatch(strings[i], "[^%s]+") do table.insert(words, match) end
|
||
|
||
-- --Если длина слов не превышает лимита
|
||
-- if unicode.len(strings[i]) <= limit then
|
||
-- table.insert(massiv, table.concat(words, " "))
|
||
-- else
|
||
-- --Перебираем все слова данной строки с 1 до конца
|
||
-- local from = 1
|
||
-- local to = 1
|
||
-- while to <= #words do
|
||
-- --Если длина соединенных слов превышает лимит, то
|
||
-- if unicode.len(table.concat(words, " ", from, to)) > limit then
|
||
-- --Вставить в новый массив строк
|
||
-- table.insert(massiv, table.concat(words, " ", from, to - 1))
|
||
-- from = to
|
||
-- else
|
||
-- if to == #words then
|
||
-- table.insert(massiv, table.concat(words, " ", from, to))
|
||
-- end
|
||
-- end
|
||
|
||
-- to = to + 1
|
||
-- end
|
||
-- end
|
||
end
|
||
|
||
return strings
|
||
end
|
||
|
||
--Моя любимая функция ошибки C:
|
||
function ECSAPI.error(...)
|
||
local args = {...}
|
||
local text = ""
|
||
if #args > 1 then
|
||
for i = 1, #args do
|
||
--text = text .. "[" .. i .. "] = " .. tostring(args[i])
|
||
if type(args[i]) == "string" then args[i] = "\"" .. args[i] .. "\"" end
|
||
text = text .. tostring(args[i])
|
||
if i ~= #args then text = text .. ", " end
|
||
end
|
||
else
|
||
text = tostring(args[1])
|
||
end
|
||
ECSAPI.universalWindow("auto", "auto", math.ceil(gpu.getResolution() * 0.45), ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x880000, "Ошибка!"}, {"EmptyLine"}, {"WrappedText", 0x262626, text}, {"EmptyLine"}, {"Button", {0x880000, 0xffffff, "OK!"}})
|
||
end
|
||
|
||
--Очистить экран, установить комфортные цвета и поставить курсок на 1, 1
|
||
function ECSAPI.prepareToExit(color1, color2)
|
||
ECSAPI.clearScreen(color1 or 0x333333)
|
||
gpu.setForeground(color2 or 0xffffff)
|
||
gpu.set(1, 1, "")
|
||
end
|
||
|
||
--Конвертация из юникода в символ. Вроде норм, а вроде и не норм. Но полезно.
|
||
function ECSAPI.convertCodeToSymbol(code)
|
||
local symbol
|
||
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
|
||
symbol = unicode.char(code)
|
||
if keyboard.isShiftPressed then symbol = unicode.upper(symbol) end
|
||
end
|
||
return symbol
|
||
end
|
||
|
||
--Шкала прогресса - маст-хев!
|
||
function ECSAPI.progressBar(x, y, width, height, background, foreground, percent)
|
||
local activeWidth = math.ceil(width * percent / 100)
|
||
ECSAPI.square(x, y, width, height, background)
|
||
ECSAPI.square(x, y, activeWidth, height, foreground)
|
||
end
|
||
|
||
--Окошко с прогрессбаром! Давно хотел
|
||
function ECSAPI.progressWindow(x, y, width, percent, text)
|
||
local height = 6
|
||
local barWidth = width - 6
|
||
|
||
x, y = ECSAPI.correctStartCoords(x, y, width, height)
|
||
|
||
ECSAPI.emptyWindow(x, y, width, height, " ")
|
||
ECSAPI.colorTextWithBack(x + math.floor(width / 2 - unicode.len(text) / 2), y + 4, 0x000000, ECSAPI.windowColors.background, text)
|
||
ECSAPI.progressBar(x + 3, y + 2, barWidth, 1, 0xCCCCCC, ECSAPI.colors.blue, percent)
|
||
end
|
||
|
||
--Функция для ввода текста в мини-поле.
|
||
function ECSAPI.inputText(x, y, limit, cheBiloVvedeno, background, foreground, justDrawNotEvent, maskTextWith)
|
||
limit = limit or 10
|
||
cheBiloVvedeno = cheBiloVvedeno or ""
|
||
background = background or 0xffffff
|
||
foreground = foreground or 0x000000
|
||
|
||
gpu.setBackground(background)
|
||
gpu.setForeground(foreground)
|
||
gpu.fill(x, y, limit, 1, " ")
|
||
|
||
local text = cheBiloVvedeno
|
||
|
||
local function draw()
|
||
term.setCursorBlink(false)
|
||
|
||
local dlina = unicode.len(text)
|
||
local xCursor = x + dlina
|
||
if xCursor > (x + limit - 1) then xCursor = (x + limit - 1) end
|
||
|
||
if maskTextWith then
|
||
gpu.set(x, y, ECSAPI.stringLimit("start", string.rep("●", dlina), limit))
|
||
else
|
||
gpu.set(x, y, ECSAPI.stringLimit("start", text, limit))
|
||
end
|
||
|
||
term.setCursor(xCursor, y)
|
||
|
||
term.setCursorBlink(true)
|
||
end
|
||
|
||
draw()
|
||
|
||
if justDrawNotEvent then term.setCursorBlink(false); return cheBiloVvedeno end
|
||
|
||
while true do
|
||
local e = {event.pull()}
|
||
if e[1] == "key_down" then
|
||
if e[4] == 14 then
|
||
term.setCursorBlink(false)
|
||
text = unicode.sub(text, 1, -2)
|
||
if unicode.len(text) < limit then gpu.set(x + unicode.len(text), y, " ") end
|
||
draw()
|
||
elseif e[4] == 28 then
|
||
term.setCursorBlink(false)
|
||
return text
|
||
else
|
||
local symbol = ECSAPI.convertCodeToSymbol(e[3])
|
||
if symbol then
|
||
text = text..symbol
|
||
draw()
|
||
end
|
||
end
|
||
elseif e[1] == "touch" then
|
||
term.setCursorBlink(false)
|
||
return text
|
||
elseif e[1] == "clipboard" then
|
||
if e[3] then
|
||
text = text..e[3]
|
||
draw()
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
--Функция парсинга сообщения об ошибке. Конвертирует из строки в массив и переводит на русский.
|
||
function ECSAPI.parseErrorMessage(error, translate)
|
||
|
||
local parsedError = {}
|
||
|
||
--ПОИСК ЭНТЕРОВ
|
||
local starting, ending, searchFrom = nil, nil, 1
|
||
for i = 1, unicode.len(error) do
|
||
starting, ending = string.find(error, "\n", searchFrom)
|
||
if starting then
|
||
table.insert(parsedError, unicode.sub(error, searchFrom, starting - 1))
|
||
searchFrom = ending + 1
|
||
else
|
||
break
|
||
end
|
||
end
|
||
|
||
--На всякий случай, если сообщение об ошибке без энтеров вообще, т.е. однострочное
|
||
if #parsedError == 0 and error ~= "" and error ~= nil and error ~= " " then
|
||
table.insert(parsedError, error)
|
||
end
|
||
|
||
--Замена /r/n и табсов
|
||
for i = 1, #parsedError do
|
||
parsedError[i] = string.gsub(parsedError[i], "\r\n", "\n")
|
||
parsedError[i] = string.gsub(parsedError[i], " ", " ")
|
||
end
|
||
|
||
if translate then
|
||
for i = 1, #parsedError do
|
||
parsedError[i] = string.gsub(parsedError[i], "interrupted", "Выполнение программы прервано пользователем")
|
||
parsedError[i] = string.gsub(parsedError[i], " got ", " получена ")
|
||
parsedError[i] = string.gsub(parsedError[i], " expected,", " ожидается,")
|
||
parsedError[i] = string.gsub(parsedError[i], "bad argument #", "Неверный аргумент #")
|
||
parsedError[i] = string.gsub(parsedError[i], "stack traceback", "Отслеживание ошибки")
|
||
parsedError[i] = string.gsub(parsedError[i], "tail calls", "Дочерние вызовы")
|
||
parsedError[i] = string.gsub(parsedError[i], "in function", "в функции")
|
||
parsedError[i] = string.gsub(parsedError[i], "in main chunk", "в основной программе")
|
||
parsedError[i] = string.gsub(parsedError[i], "unexpected symbol near", "неожиданный символ рядом с")
|
||
parsedError[i] = string.gsub(parsedError[i], "attempt to index", "пытаюсь получить значение индекса массива")
|
||
parsedError[i] = string.gsub(parsedError[i], "attempt to get length of", "не удается получить длину")
|
||
parsedError[i] = string.gsub(parsedError[i], ": ", ", ")
|
||
parsedError[i] = string.gsub(parsedError[i], " module ", " модуль ")
|
||
parsedError[i] = string.gsub(parsedError[i], "not found", "не найден")
|
||
parsedError[i] = string.gsub(parsedError[i], "no field package.preload", "не найдена библиотека")
|
||
parsedError[i] = string.gsub(parsedError[i], "no file", "нет файла")
|
||
parsedError[i] = string.gsub(parsedError[i], "local", "локальной")
|
||
parsedError[i] = string.gsub(parsedError[i], "global", "глобальной")
|
||
parsedError[i] = string.gsub(parsedError[i], "no primary", "не найден компонент")
|
||
parsedError[i] = string.gsub(parsedError[i], "available", "в доступе")
|
||
parsedError[i] = string.gsub(parsedError[i], "attempt to concatenate", "не могу присоединить")
|
||
parsedError[i] = string.gsub(parsedError[i], "a nil value", "переменная равна nil")
|
||
end
|
||
end
|
||
|
||
starting, ending = nil, nil
|
||
|
||
return parsedError
|
||
end
|
||
|
||
--Отображение сообщения об ошибке компиляции скрипта в красивом окошке.
|
||
function ECSAPI.displayCompileMessage(y, reason, translate, withAnimation)
|
||
|
||
local xSize, ySize = gpu.getResolution()
|
||
|
||
--Переводим причину в массив
|
||
reason = ECSAPI.parseErrorMessage(reason, translate)
|
||
|
||
--Получаем ширину и высоту окошка
|
||
local width = math.floor(xSize * 7 / 10)
|
||
local textWidth = width - 11
|
||
reason = ECSAPI.stringWrap(reason, textWidth)
|
||
local height = #reason + 6
|
||
|
||
--Просчет вот этой хуйни, аааахаахах
|
||
local difference = ySize - (height + y)
|
||
if difference < 0 then
|
||
for i = 1, (math.abs(difference) + 1) do
|
||
table.remove(reason, #reason)
|
||
end
|
||
table.insert(reason, "…")
|
||
height = #reason + 6
|
||
end
|
||
|
||
local x = math.floor(xSize / 2 - width / 2)
|
||
|
||
--Иконочка воскл знака на красном фоне
|
||
local errorImage = {
|
||
{{0xff0000,0xffffff,"#"},{0xff0000,0xffffff,"#"},{0xff0000,0xffffff," "},{0xff0000,0xffffff,"#"},{0xff0000,0xffffff,"#"}},
|
||
{{0xff0000,0xffffff,"#"},{0xff0000,0xffffff," "},{0xff0000,0xffffff,"!"},{0xff0000,0xffffff," "},{0xff0000,0xffffff,"#"}},
|
||
{{0xff0000,0xffffff," "},{0xff0000,0xffffff," "},{0xff0000,0xffffff," "},{0xff0000,0xffffff," "},{0xff0000,0xffffff," "}}
|
||
}
|
||
|
||
--Запоминаем, че было отображено
|
||
local oldPixels
|
||
|
||
local function drawCompileMessage(y)
|
||
ECSAPI.square(x, y, width, height, ECSAPI.windowColors.background)
|
||
ECSAPI.windowShadow(x, y, width, height)
|
||
--Рисуем воскл знак
|
||
ECSAPI.drawCustomImage(x + 2, y + 1, errorImage)
|
||
|
||
--Рисуем текст
|
||
local yPos = y + 1
|
||
local xPos = x + 9
|
||
gpu.setBackground(ECSAPI.windowColors.background)
|
||
|
||
ECSAPI.colorText(xPos, yPos, ECSAPI.windowColors.usualText, "Код ошибки:")
|
||
yPos = yPos + 2
|
||
|
||
gpu.setForeground( 0xcc0000 )
|
||
for i = 1, #reason do
|
||
gpu.set(xPos, yPos, reason[i])
|
||
yPos = yPos + 1
|
||
end
|
||
|
||
yPos = yPos + 1
|
||
ECSAPI.colorText(xPos, yPos, ECSAPI.windowColors.usualText, ECSAPI.stringLimit("end", "Нажмите любую клавишу, чтобы продолжить", textWidth))
|
||
end
|
||
|
||
--Типа анимация, ога
|
||
if withAnimation then
|
||
oldPixels = ECSAPI.rememberOldPixels(x, 1, x + width + 1, height + 1)
|
||
for i = -height, 1, 1 do
|
||
drawCompileMessage(i)
|
||
os.sleep(0.01)
|
||
end
|
||
else
|
||
oldPixels = ECSAPI.rememberOldPixels(x, y, x + width + 1, y + height)
|
||
drawCompileMessage(y)
|
||
end
|
||
|
||
--Пикаем звуком кароч
|
||
for i = 1, 3 do
|
||
computer.beep(1000)
|
||
end
|
||
--Ждем сам знаешь чего
|
||
ECSAPI.wait()
|
||
--Рисуем, че было нарисовано
|
||
ECSAPI.drawOldPixels(oldPixels)
|
||
end
|
||
|
||
--Спросить, заменять ли файл (если таковой уже имеется)
|
||
function ECSAPI.askForReplaceFile(path)
|
||
if fs.exists(path) then
|
||
local action = ECSAPI.universalWindow("auto", "auto", 46, ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Файл \"".. fs.name(path) .. "\" уже имеется в этом месте."}, {"CenterText", 0x262626, "Заменить его перемещаемым объектом?"}, {"EmptyLine"}, {"Button", {0xdddddd, 0x262626, "Оставить оба"}, {0xffffff, 0x262626, "Отмена"}, {ECSAPI.colors.lightBlue, 0xffffff, "Заменить"}})
|
||
if action[1] == "Оставить оба" then
|
||
return "keepBoth"
|
||
elseif action[2] == "Отмена" then
|
||
return "cancel"
|
||
else
|
||
return "replace"
|
||
end
|
||
end
|
||
end
|
||
|
||
--Проверить имя файла на соответствие критериям
|
||
function ECSAPI.checkName(name, path)
|
||
--Если ввели хуйню какую-то, то
|
||
if name == "" or name == " " or name == nil then
|
||
ECSAPI.error("Неверное имя файла.")
|
||
return false
|
||
else
|
||
--Если файл с новым путем уже существует, то
|
||
if fs.exists(path .. name) then
|
||
ECSAPI.error("Файл \"".. name .. "\" уже имеется в этом месте.")
|
||
return false
|
||
--А если все заебок, то
|
||
else
|
||
return true
|
||
end
|
||
end
|
||
end
|
||
|
||
--Переименование файлов (для операционки)
|
||
function ECSAPI.rename(mainPath)
|
||
--Задаем стартовую щнягу
|
||
local name = fs.name(mainPath)
|
||
path = fs.path(mainPath)
|
||
--Рисуем окошко ввода нового имени файла
|
||
local inputs = ECSAPI.universalWindow("auto", "auto", 30, ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Переименовать"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, name}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
|
||
--Переименовываем
|
||
if ECSAPI.checkName(inputs[1], path) then
|
||
fs.rename(mainPath, path .. inputs[1])
|
||
end
|
||
end
|
||
|
||
--Создать новую папку (для операционки)
|
||
function ECSAPI.newFolder(path)
|
||
--Рисуем окошко ввода нового имени файла
|
||
local inputs = ECSAPI.universalWindow("auto", "auto", 30, ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новая папка"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
|
||
|
||
if ECSAPI.checkName(inputs[1], path) then
|
||
fs.makeDirectory(path .. inputs[1])
|
||
end
|
||
end
|
||
|
||
--Создать новый файл (для операционки)
|
||
function ECSAPI.newFile(path)
|
||
--Рисуем окошко ввода нового имени файла
|
||
local inputs = ECSAPI.universalWindow("auto", "auto", 30, ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новый файл"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, ""}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
|
||
|
||
if ECSAPI.checkName(inputs[1], path) then
|
||
ECSAPI.prepareToExit()
|
||
ECSAPI.editFile(path .. inputs[1])
|
||
end
|
||
end
|
||
|
||
--Создать новое приложение (для операционки)
|
||
function ECSAPI.newApplication(path, startName)
|
||
--Рисуем окошко ввода нового имени файла
|
||
local inputs
|
||
if not startName then
|
||
inputs = ECSAPI.universalWindow("auto", "auto", 30, ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Новое приложение"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Введите имя"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
|
||
end
|
||
|
||
if ECSAPI.checkName(inputs[1] .. ".app", path) then
|
||
local name = path .. inputs[1] .. ".app/Resources/"
|
||
fs.makeDirectory(name)
|
||
fs.copy("MineOS/System/OS/Icons/SampleIcon.pic", name .. "Icon.pic")
|
||
local file = io.open(path .. inputs[1] .. ".app/" .. inputs[1] .. ".lua", "w")
|
||
file:write("local ecs = require(\"ECSAPI\")", "\n")
|
||
file:write("ecs.universalWindow(\"auto\", \"auto\", 30, 0xeeeeee, true, {\"EmptyLine\"}, {\"CenterText\", 0x262626, \"Hello world!\"}, {\"EmptyLine\"}, {\"Button\", {0x880000, 0xffffff, \"Hello!\"}})", "\n")
|
||
file:close()
|
||
end
|
||
end
|
||
|
||
--Создать приложение на основе существующего ЛУА-файла
|
||
function ECSAPI.newApplicationFromLuaFile(pathToLuaFile, pathWhereToCreateApplication)
|
||
local data = ECSAPI.universalWindow("auto", "auto", 30, ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x000000, "Новое приложение"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Имя приложения"}, {"Input", 0x262626, 0x880000, "Путь к иконке приложения"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK"}})
|
||
data[1] = data[1] or "MyApplication"
|
||
data[2] = data[2] or "MineOS/System/OS/Icons/SampleIcon.pic"
|
||
if fs.exists(data[2]) then
|
||
fs.makeDirectory(pathWhereToCreateApplication .. "/" .. data[1] .. ".app/Resources")
|
||
fs.copy(pathToLuaFile, pathWhereToCreateApplication .. "/" .. data[1] .. ".app/" .. data[1] .. ".lua")
|
||
fs.copy(data[2], pathWhereToCreateApplication .. "/" .. data[1] .. ".app/Resources/Icon.pic")
|
||
|
||
--ECSAPI.universalWindow("auto", "auto", 30, ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x000000, "Приложение создано!"}, {"EmptyLine"}, {"Button", {ecs.colors.green, 0xffffff, "OK"}})
|
||
else
|
||
ECSAPI.error("Указанный файл иконки не существует.")
|
||
return
|
||
end
|
||
end
|
||
|
||
--Простое информационное окошечко. Возвращает старые пиксели - мало ли понадобится.
|
||
function ECSAPI.info(x, y, title, text)
|
||
x = x or "auto"
|
||
y = y or "auto"
|
||
title = title or " "
|
||
text = text or "Sample text"
|
||
|
||
local width = unicode.len(text) + 4
|
||
local height = 4
|
||
x, y = ECSAPI.correctStartCoords(x, y, width, height)
|
||
|
||
local oldPixels = ECSAPI.rememberOldPixels(x, y, x + width + 1, y + height)
|
||
|
||
ECSAPI.emptyWindow(x, y, width, height, title)
|
||
ECSAPI.colorTextWithBack(x + 2, y + 2, ECSAPI.windowColors.usualText, ECSAPI.windowColors.background, text)
|
||
|
||
return oldPixels
|
||
end
|
||
|
||
--Вертикальный скроллбар. Маст-хев!
|
||
function ECSAPI.srollBar(x, y, width, height, countOfAllElements, currentElement, backColor, frontColor)
|
||
local sizeOfScrollBar = math.ceil(1 / countOfAllElements * height)
|
||
local displayBarFrom = math.floor(y + height * ((currentElement - 1) / countOfAllElements))
|
||
|
||
ECSAPI.square(x, y, width, height, backColor)
|
||
ECSAPI.square(x, displayBarFrom, width, sizeOfScrollBar, frontColor)
|
||
|
||
sizeOfScrollBar, displayBarFrom = nil, nil
|
||
end
|
||
|
||
--Отрисовка поля с текстом. Сюда пихать массив вида {"строка1", "строка2", "строка3", ...}
|
||
function ECSAPI.textField(x, y, width, height, lines, displayFrom, background, foreground, scrollbarBackground, scrollbarForeground)
|
||
x, y = ECSAPI.correctStartCoords(x, y, width, height)
|
||
|
||
background = background or 0xffffff
|
||
foreground = foreground or ECSAPI.windowColors.usualText
|
||
|
||
local sLines = #lines
|
||
local lineLimit = width - 3
|
||
|
||
--Парсим строки
|
||
local line = 1
|
||
while lines[line] do
|
||
local sLine = unicode.len(lines[line])
|
||
if sLine > lineLimit then
|
||
local part1, part2 = unicode.sub(lines[line], 1, lineLimit), unicode.sub(lines[line], lineLimit + 1, -1)
|
||
lines[line] = part1
|
||
table.insert(lines, line + 1, part2)
|
||
part1, part2 = nil, nil
|
||
end
|
||
line = line + 1
|
||
sLine = nil
|
||
end
|
||
line = nil
|
||
|
||
ECSAPI.square(x, y, width - 1, height, background)
|
||
ECSAPI.srollBar(x + width - 1, y, 1, height, sLines, displayFrom, scrollbarBackground, scrollbarForeground)
|
||
|
||
gpu.setBackground(background)
|
||
gpu.setForeground(foreground)
|
||
local yPos = y
|
||
for i = displayFrom, (displayFrom + height - 1) do
|
||
if lines[i] then
|
||
gpu.set(x + 1, yPos, lines[i])
|
||
yPos = yPos + 1
|
||
else
|
||
break
|
||
end
|
||
end
|
||
|
||
return sLines
|
||
end
|
||
|
||
--Получение верного имени языка. Просто для безопасности. (для операционки)
|
||
function ECSAPI.getCorrectLangName(pathToLangs)
|
||
local language = _G.OSSettings.language .. ".lang"
|
||
if not fs.exists(pathToLangs .. "/" .. language) then
|
||
language = "English.lang"
|
||
end
|
||
return language
|
||
end
|
||
|
||
--Чтение языкового файла (для операционки)
|
||
function ECSAPI.readCorrectLangFile(pathToLangs)
|
||
local lang
|
||
|
||
local language = ECSAPI.getCorrectLangName(pathToLangs)
|
||
|
||
lang = config.readAll(pathToLangs .. "/" .. language)
|
||
|
||
return lang
|
||
end
|
||
|
||
-------------------------ВСЕ ДЛЯ ОСКИ-------------------------------------------------------------------------------
|
||
|
||
function ECSAPI.sortFiles(path, fileList, sortingMethod, showHiddenFiles)
|
||
local sortedFileList = {}
|
||
if sortingMethod == "type" then
|
||
local typeList = {}
|
||
for i = 1, #fileList do
|
||
local fileFormat = ECSAPI.getFileFormat(fileList[i]) or "Script"
|
||
if fs.isDirectory(path .. fileList[i]) and fileFormat ~= ".app" then fileFormat = "Folder" end
|
||
typeList[fileFormat] = typeList[fileFormat] or {}
|
||
table.insert(typeList[fileFormat], fileList[i])
|
||
end
|
||
|
||
if typeList["Folder"] then
|
||
for i = 1, #typeList["Folder"] do
|
||
table.insert(sortedFileList, typeList["Folder"][i])
|
||
end
|
||
typeList["Folder"] = nil
|
||
end
|
||
|
||
for fileFormat in pairs(typeList) do
|
||
for i = 1, #typeList[fileFormat] do
|
||
table.insert(sortedFileList, typeList[fileFormat][i])
|
||
end
|
||
end
|
||
elseif sortingMethod == "name" then
|
||
sortedFileList = fileList
|
||
elseif sortingMethod == "date" then
|
||
for i = 1, #fileList do
|
||
fileList[i] = {fileList[i], fs.lastModified(path .. fileList[i])}
|
||
end
|
||
table.sort(fileList, function(a,b) return a[2] > b[2] end)
|
||
for i = 1, #fileList do
|
||
table.insert(sortedFileList, fileList[i][1])
|
||
end
|
||
else
|
||
error("Unknown sorting method")
|
||
end
|
||
|
||
local i = 1
|
||
while i <= #sortedFileList do
|
||
if not showHiddenFiles and ECSAPI.isFileHidden(sortedFileList[i]) then
|
||
table.remove(sortedFileList, i)
|
||
else
|
||
i = i + 1
|
||
end
|
||
end
|
||
|
||
return sortedFileList
|
||
end
|
||
|
||
--Сохранить файл конфигурации ОС
|
||
function ECSAPI.saveOSSettings()
|
||
local pathToOSSettings = "MineOS/System/OS/OSSettings.cfg"
|
||
if not _G.OSSettings then error("Массив настроек ОС отсутствует в памяти!") end
|
||
fs.makeDirectory(fs.path(pathToOSSettings))
|
||
local file = io.open(pathToOSSettings, "w")
|
||
file:write(serialization.serialize(_G.OSSettings))
|
||
file:close()
|
||
end
|
||
|
||
--Загрузить файл конфигурации ОС, а если его не существует, то создать
|
||
function ECSAPI.loadOSSettings()
|
||
local pathToOSSettings = "MineOS/System/OS/OSSettings.cfg"
|
||
if fs.exists(pathToOSSettings) then
|
||
local file = io.open(pathToOSSettings, "r")
|
||
_G.OSSettings = serialization.unserialize(file:read("*a"))
|
||
file:close()
|
||
else
|
||
_G.OSSettings = { showHelpOnApplicationStart = true, language = "Russian" }
|
||
ECSAPI.saveOSSettings()
|
||
end
|
||
end
|
||
|
||
--Отобразить окно с содержимым файла информации о приложении
|
||
function ECSAPI.applicationHelp(pathToApplication)
|
||
local pathToAboutFile = pathToApplication .. "/resources/About.txt"
|
||
if _G.OSSettings and _G.OSSettings.showHelpOnApplicationStart and fs.exists(pathToAboutFile) then
|
||
local applicationName = fs.name(pathToApplication)
|
||
local file = io.open(pathToAboutFile, "r")
|
||
local text = ""
|
||
for line in file:lines() do text = text .. line .. " " end
|
||
file:close()
|
||
|
||
local data = ECSAPI.universalWindow("auto", "auto", 30, 0xeeeeee, true,
|
||
{"EmptyLine"},
|
||
{"CenterText", 0x000000, "О приложении " .. applicationName},
|
||
{"EmptyLine"},
|
||
{"TextField", 16, 0xFFFFFF, 0x262626, 0xcccccc, 0x353535, text},
|
||
{"EmptyLine"},
|
||
{"Button", {ECSAPI.colors.orange, 0x262626, "OK"}, {0x999999, 0xffffff, "Больше не показывать"}}
|
||
)
|
||
if data[1] ~= "OK" then
|
||
_G.OSSettings.showHelpOnApplicationStart = false
|
||
ECSAPI.saveOSSettings()
|
||
end
|
||
end
|
||
end
|
||
|
||
--Создать ярлык для конкретной проги (для операционки)
|
||
function ECSAPI.createShortCut(path, pathToProgram)
|
||
fs.remove(path)
|
||
fs.makeDirectory(fs.path(path))
|
||
local file = io.open(path, "w")
|
||
file:write("return ", "\"", pathToProgram, "\"")
|
||
file:close()
|
||
end
|
||
|
||
--Получить данные о файле из ярлыка (для операционки)
|
||
function ECSAPI.readShortcut(path)
|
||
local success, filename = pcall(loadfile(path))
|
||
if success then
|
||
return filename
|
||
else
|
||
error("Ошибка чтения файла ярлыка. Вероятно, он создан криво, либо не существует в папке " .. path)
|
||
end
|
||
end
|
||
|
||
--Редактирование файла (для операционки)
|
||
function ECSAPI.editFile(path)
|
||
ECSAPI.prepareToExit()
|
||
shell.execute("edit "..path)
|
||
end
|
||
|
||
-- Копирование папки через рекурсию, т.к. fs.copy() не поддерживает папки
|
||
-- Ну долбоеб автор мода - хули я тут сделаю? Придется так вот
|
||
-- Хотя можно юзать обычный bin/cp, как это сделано в дисковом дубляже. Надо перекодить, короч
|
||
function ECSAPI.copyFolder(path, toPath)
|
||
local function doCopy(path)
|
||
local fileList = ECSAPI.getFileList(path)
|
||
for i = 1, #fileList do
|
||
if fs.isDirectory(path..fileList[i]) then
|
||
doCopy(path..fileList[i])
|
||
else
|
||
fs.makeDirectory(toPath..path)
|
||
fs.copy(path..fileList[i], toPath ..path.. fileList[i])
|
||
end
|
||
end
|
||
end
|
||
|
||
toPath = fs.path(toPath)
|
||
doCopy(path.."/")
|
||
end
|
||
|
||
--Копирование файлов для операционки
|
||
function ECSAPI.copy(from, to)
|
||
local name = fs.name(from)
|
||
local toName = to .. "/" .. name
|
||
local action = ECSAPI.askForReplaceFile(toName)
|
||
if action == nil or action == "replace" then
|
||
fs.remove(toName)
|
||
if fs.isDirectory(from) then
|
||
ECSAPI.copyFolder(from, toName)
|
||
else
|
||
fs.copy(from, toName)
|
||
end
|
||
elseif action == "keepBoth" then
|
||
if fs.isDirectory(from) then
|
||
ECSAPI.copyFolder(from, to .. "/(copy)" .. name)
|
||
else
|
||
fs.copy(from, to .. "/(copy)" .. name)
|
||
end
|
||
end
|
||
end
|
||
|
||
ECSAPI.OSIconsWidth = 12
|
||
ECSAPI.OSIconsHeight = 6
|
||
|
||
--Вся необходимая информация для иконок
|
||
local function OSIconsInit()
|
||
if not _G.image then _G.image = require("image") end
|
||
if not _G.buffer then _G.buffer = require("doubleBuffering") end
|
||
if not ECSAPI.OSIcons then
|
||
--Константы для иконок
|
||
ECSAPI.OSIcons = {}
|
||
ECSAPI.pathToIcons = "MineOS/System/OS/Icons/"
|
||
|
||
--Иконки
|
||
ECSAPI.OSIcons.folder = image.load(ECSAPI.pathToIcons .. "Folder.pic")
|
||
ECSAPI.OSIcons.script = image.load(ECSAPI.pathToIcons .. "Script.pic")
|
||
ECSAPI.OSIcons.text = image.load(ECSAPI.pathToIcons .. "Text.pic")
|
||
ECSAPI.OSIcons.config = image.load(ECSAPI.pathToIcons .. "Config.pic")
|
||
ECSAPI.OSIcons.lua = image.load(ECSAPI.pathToIcons .. "Lua.pic")
|
||
ECSAPI.OSIcons.image = image.load(ECSAPI.pathToIcons .. "Image.pic")
|
||
ECSAPI.OSIcons.imageJPG = image.load(ECSAPI.pathToIcons .. "RawImage.pic")
|
||
ECSAPI.OSIcons.pastebin = image.load(ECSAPI.pathToIcons .. "Pastebin.pic")
|
||
ECSAPI.OSIcons.fileNotExists = image.load(ECSAPI.pathToIcons .. "FileNotExists.pic")
|
||
ECSAPI.OSIcons.archive = image.load(ECSAPI.pathToIcons .. "Archive.pic")
|
||
ECSAPI.OSIcons.model3D = image.load(ECSAPI.pathToIcons .. "3DModel.pic")
|
||
end
|
||
end
|
||
|
||
--Отрисовка одной иконки
|
||
function ECSAPI.drawOSIcon(x, y, path, showFileFormat, nameColor)
|
||
--Инициализируем переменные иконок. Чисто для уменьшения расхода оперативки.
|
||
OSIconsInit()
|
||
--Получаем формат файла
|
||
local fileFormat = ECSAPI.getFileFormat(path)
|
||
--Создаем пустую переменную для конкретной иконки, для ее типа
|
||
local icon
|
||
--Если данный файл является папкой, то
|
||
if fs.isDirectory(path) then
|
||
if fileFormat == ".app" then
|
||
icon = path .. "/Resources/Icon.pic"
|
||
--Если данной иконки еще нет в оперативке, то загрузить ее
|
||
if not ECSAPI.OSIcons[icon] then
|
||
ECSAPI.OSIcons[icon] = image.load(icon)
|
||
end
|
||
else
|
||
icon = "folder"
|
||
end
|
||
else
|
||
if fileFormat == ".lnk" then
|
||
local shortcutLink = ECSAPI.readShortcut(path)
|
||
ECSAPI.drawOSIcon(x, y, shortcutLink, showFileFormat, nameColor)
|
||
--Стрелочка
|
||
buffer.set(x + ECSAPI.OSIconsWidth - 3, y + ECSAPI.OSIconsHeight - 3, 0xFFFFFF, 0x000000, "<")
|
||
return 0
|
||
elseif fileFormat == ".cfg" or fileFormat == ".config" then
|
||
icon = "config"
|
||
elseif fileFormat == ".txt" or fileFormat == ".rtf" then
|
||
icon = "text"
|
||
elseif fileFormat == ".lua" then
|
||
icon = "lua"
|
||
elseif fileFormat == ".pic" or fileFormat == ".png" then
|
||
icon = "image"
|
||
elseif fileFormat == ".rawpic" then
|
||
icon = "imageJPG"
|
||
elseif fileFormat == ".paste" then
|
||
icon = "pastebin"
|
||
elseif fileFormat == ".pkg" then
|
||
icon = "archive"
|
||
elseif fileFormat == ".3dm" then
|
||
icon = "model3D"
|
||
elseif not fs.exists(path) then
|
||
icon = "fileNotExists"
|
||
else
|
||
icon = "script"
|
||
end
|
||
end
|
||
|
||
--Рисуем иконку
|
||
buffer.image(x + 2, y, ECSAPI.OSIcons[icon])
|
||
|
||
--Делаем текст для иконки
|
||
local text = fs.name(path)
|
||
if not showFileFormat and fileFormat then
|
||
text = unicode.sub(text, 1, -(unicode.len(fileFormat) + 1))
|
||
end
|
||
text = ECSAPI.stringLimit("end", text, ECSAPI.OSIconsWidth)
|
||
--Рассчитываем позицию текста
|
||
local textPos = x + math.floor(ECSAPI.OSIconsWidth / 2 - unicode.len(text) / 2)
|
||
--Рисуем текст под иконкой
|
||
buffer.text(textPos, y + ECSAPI.OSIconsHeight - 1, nameColor or 0xffffff, text)
|
||
|
||
end
|
||
|
||
--ЗАПУСТИТЬ ПРОГУ
|
||
function ECSAPI.launchIcon(path, arguments)
|
||
local withAnimation = false
|
||
local translate = true
|
||
--Запоминаем, какое разрешение было
|
||
local oldWidth, oldHeight = gpu.getResolution()
|
||
--Создаем нормальные аргументы для Шелла
|
||
if arguments then arguments = " " .. arguments else arguments = "" end
|
||
--Получаем файл формат заранее
|
||
local fileFormat = ECSAPI.getFileFormat(path)
|
||
local isDirectory = fs.isDirectory(path)
|
||
--Если это приложение
|
||
if fileFormat == ".app" then
|
||
ECSAPI.applicationHelp(path)
|
||
local cyka = path .. "/" .. ECSAPI.hideFileFormat(fs.name(path)) .. ".lua"
|
||
local success, reason = shell.execute(cyka)
|
||
if not success then ECSAPI.displayCompileMessage(1, reason, translate, withAnimation) end
|
||
--Если это папка
|
||
elseif (fileFormat == "" or fileFormat == nil) and isDirectory then
|
||
shell.execute("MineOS/Applications/Finder.app/Finder.lua " .. path)
|
||
--Если это обычный луа файл - т.е. скрипт
|
||
elseif fileFormat == ".lua" or fileFormat == nil then
|
||
ECSAPI.prepareToExit()
|
||
local success, reason = shell.execute(path .. arguments)
|
||
if success then
|
||
print(" ")
|
||
print("Program sucessfully executed. Press any key to continue.")
|
||
print(" ")
|
||
else
|
||
ECSAPI.displayCompileMessage(1, reason, translate, withAnimation)
|
||
end
|
||
--Если это фоточка
|
||
elseif fileFormat == ".pic" then
|
||
shell.execute("MineOS/Applications/Photoshop.app/Photoshop.lua open " .. path)
|
||
--Если это 3D-модель
|
||
elseif fileFormat == ".3dm" then
|
||
shell.execute("MineOS/Applications/3DPrint.app/3DPrint.lua open " .. path)
|
||
--Если это фоточка
|
||
elseif fileFormat == ".raw" then
|
||
shell.execute("MineOS/Applications/Photoshop.app/Photoshop.lua open " .. path)
|
||
--Если это текст или конфиг или языковой
|
||
elseif fileFormat == ".txt" or fileFormat == ".cfg" or fileFormat == ".lang" then
|
||
ECSAPI.prepareToExit()
|
||
shell.execute("edit "..path)
|
||
--Если это ярлык
|
||
elseif fileFormat == ".lnk" then
|
||
local shortcutLink = ECSAPI.readShortcut(path)
|
||
if fs.exists(shortcutLink) then
|
||
ECSAPI.launchIcon(shortcutLink)
|
||
else
|
||
ECSAPI.error("File from shortcut link doesn't exists.")
|
||
end
|
||
--Если это ссылка на пастебин
|
||
elseif fileFormat == ".paste" then
|
||
local shortcutLink = ECSAPI.readShortcut(path)
|
||
ECSAPI.prepareToExit()
|
||
local success, reason = shell.execute("pastebin run " .. shortcutLink)
|
||
if success then
|
||
print(" ")
|
||
print("Program sucessfully executed. Press any key to continue.")
|
||
ECSAPI.waitForTouchOrClick()
|
||
else
|
||
ECSAPI.displayCompileMessage(1, reason, translate, withAnimation)
|
||
end
|
||
--Если это архив
|
||
elseif fileFormat == ".zip" then
|
||
zip.unarchive(path, (fs.path(path) or ""))
|
||
end
|
||
--Ставим старое разрешение
|
||
gpu.setResolution(oldWidth, oldHeight)
|
||
end
|
||
|
||
|
||
|
||
|
||
---------------------------------------------ОКОШЕЧКИ------------------------------------------------------------
|
||
|
||
|
||
--Описание ниже, ебана. Ниже - это значит в самой жопе кода!
|
||
function ECSAPI.universalWindow(x, y, width, background, closeWindowAfter, ...)
|
||
local objects = {...}
|
||
local countOfObjects = #objects
|
||
|
||
local pressedButton
|
||
local pressedMultiButton
|
||
|
||
--Задаем высотные константы для объектов
|
||
local objectsHeights = {
|
||
["button"] = 3,
|
||
["centertext"] = 1,
|
||
["emptyline"] = 1,
|
||
["input"] = 3,
|
||
["slider"] = 3,
|
||
["select"] = 3,
|
||
["selector"] = 3,
|
||
["separator"] = 1,
|
||
["switch"] = 1,
|
||
["color"] = 3,
|
||
}
|
||
|
||
--Скорректировать ширину, если нужно
|
||
local function correctWidth(newWidthForAnalyse)
|
||
width = math.max(width, newWidthForAnalyse)
|
||
end
|
||
|
||
--Корректируем ширину
|
||
for i = 1, countOfObjects do
|
||
local objectType = string.lower(objects[i][1])
|
||
|
||
if objectType == "centertext" then
|
||
correctWidth(unicode.len(objects[i][3]) + 2)
|
||
elseif objectType == "slider" then --!!!!!!!!!!!!!!!!!! ВОТ ТУТ НЕ ЗАБУДЬ ФИКСАНУТЬ
|
||
correctWidth(unicode.len(objects[i][7]..tostring(objects[i][5].." ")) + 2)
|
||
elseif objectType == "select" then
|
||
for j = 4, #objects[i] do
|
||
correctWidth(unicode.len(objects[i][j]) + 2)
|
||
end
|
||
--elseif objectType == "selector" then
|
||
|
||
--elseif objectType == "separator" then
|
||
|
||
elseif objectType == "textfield" then
|
||
correctWidth(5)
|
||
elseif objectType == "wrappedtext" then
|
||
correctWidth(6)
|
||
elseif objectType == "button" then
|
||
--Корректируем ширину
|
||
local widthOfButtons = 0
|
||
local maxButton = 0
|
||
for j = 2, #objects[i] do
|
||
maxButton = math.max(maxButton, unicode.len(objects[i][j][3]) + 2)
|
||
end
|
||
widthOfButtons = maxButton * #objects[i]
|
||
correctWidth(widthOfButtons)
|
||
elseif objectType == "switch" then
|
||
local dlina = unicode.len(objects[i][5]) + 2 + 10 + 4
|
||
correctWidth(dlina)
|
||
elseif objectType == "color" then
|
||
correctWidth(unicode.len(objects[i][2]) + 6)
|
||
end
|
||
end
|
||
|
||
--Считаем высоту этой хуйни
|
||
local height = 0
|
||
for i = 1, countOfObjects do
|
||
local objectType = string.lower(objects[i][1])
|
||
if objectType == "select" then
|
||
height = height + (objectsHeights[objectType] * (#objects[i] - 3))
|
||
elseif objectType == "textfield" then
|
||
height = height + objects[i][2]
|
||
elseif objectType == "wrappedtext" then
|
||
--Заранее парсим текст перенесенный
|
||
objects[i].wrapped = ECSAPI.stringWrap({objects[i][3]}, width - 4)
|
||
objects[i].height = #objects[i].wrapped
|
||
height = height + objects[i].height
|
||
else
|
||
height = height + objectsHeights[objectType]
|
||
end
|
||
end
|
||
|
||
--Коорректируем стартовые координаты
|
||
x, y = ECSAPI.correctStartCoords(x, y, width, height)
|
||
--Запоминаем инфу о том, что было нарисовано, если это необходимо
|
||
local oldPixels, oldBackground, oldForeground
|
||
if closeWindowAfter then
|
||
oldBackground = gpu.getBackground()
|
||
oldForeground = gpu.getForeground()
|
||
oldPixels = ECSAPI.rememberOldPixels(x, y, x + width - 1, y + height - 1)
|
||
end
|
||
--Считаем все координаты объектов
|
||
objects[1].y = y
|
||
if countOfObjects > 1 then
|
||
for i = 2, countOfObjects do
|
||
local objectType = string.lower(objects[i - 1][1])
|
||
if objectType == "select" then
|
||
objects[i].y = objects[i - 1].y + (objectsHeights[objectType] * (#objects[i - 1] - 3))
|
||
elseif objectType == "textfield" then
|
||
objects[i].y = objects[i - 1].y + objects[i - 1][2]
|
||
elseif objectType == "wrappedtext" then
|
||
objects[i].y = objects[i - 1].y + objects[i - 1].height
|
||
else
|
||
objects[i].y = objects[i - 1].y + objectsHeights[objectType]
|
||
end
|
||
end
|
||
end
|
||
|
||
--Объекты для тача
|
||
local obj = {}
|
||
local function newObj(class, name, ...)
|
||
obj[class] = obj[class] or {}
|
||
obj[class][name] = {...}
|
||
end
|
||
|
||
--Отображение объекта по номеру
|
||
local function displayObject(number, active)
|
||
local objectType = string.lower(objects[number][1])
|
||
|
||
if objectType == "centertext" then
|
||
local xPos = x + math.floor(width / 2 - unicode.len(objects[number][3]) / 2)
|
||
gpu.setForeground(objects[number][2])
|
||
gpu.setBackground(background)
|
||
gpu.set(xPos, objects[number].y, objects[number][3])
|
||
|
||
elseif objectType == "input" then
|
||
|
||
if active then
|
||
--Рамочка
|
||
ECSAPI.border(x + 1, objects[number].y, width - 2, objectsHeights.input, background, objects[number][3])
|
||
--Тестик
|
||
objects[number][4] = ECSAPI.inputText(x + 3, objects[number].y + 1, width - 6, "", background, objects[number][3], false, objects[number][5])
|
||
else
|
||
--Рамочка
|
||
ECSAPI.border(x + 1, objects[number].y, width - 2, objectsHeights.input, background, objects[number][2])
|
||
--Текстик
|
||
gpu.set(x + 3, objects[number].y + 1, ECSAPI.stringLimit("start", objects[number][4], width - 6))
|
||
ECSAPI.inputText(x + 3, objects[number].y + 1, width - 6, objects[number][4], background, objects[number][2], true, objects[number][5])
|
||
end
|
||
|
||
newObj("Inputs", number, x + 1, objects[number].y, x + width - 2, objects[number].y + 2)
|
||
|
||
elseif objectType == "slider" then
|
||
local widthOfSlider = width - 2
|
||
local xOfSlider = x + 1
|
||
local yOfSlider = objects[number].y + 1
|
||
local countOfSliderThings = objects[number][5] - objects[number][4]
|
||
local showSliderValue= objects[number][7]
|
||
|
||
local dolya = widthOfSlider / countOfSliderThings
|
||
local position = math.floor(dolya * objects[number][6])
|
||
--Костыль
|
||
if (xOfSlider + position) > (xOfSlider + widthOfSlider - 1) then position = widthOfSlider - 2 end
|
||
|
||
--Две линии
|
||
ECSAPI.separator(xOfSlider, yOfSlider, position, background, objects[number][3])
|
||
ECSAPI.separator(xOfSlider + position, yOfSlider, widthOfSlider - position, background, objects[number][2])
|
||
--Слудир
|
||
ECSAPI.square(xOfSlider + position, yOfSlider, 2, 1, objects[number][3])
|
||
|
||
--Текстик под слудиром
|
||
if showSliderValue then
|
||
local text = showSliderValue .. tostring(objects[number][6]) .. (objects[number][8] or "")
|
||
local textPos = (xOfSlider + widthOfSlider / 2 - unicode.len(text) / 2)
|
||
ECSAPI.square(x, yOfSlider + 1, width, 1, background)
|
||
ECSAPI.colorText(textPos, yOfSlider + 1, objects[number][2], text)
|
||
end
|
||
|
||
newObj("Sliders", number, xOfSlider, yOfSlider, x + widthOfSlider, yOfSlider, dolya)
|
||
|
||
elseif objectType == "select" then
|
||
local usualColor = objects[number][2]
|
||
local selectionColor = objects[number][3]
|
||
|
||
objects[number].selectedData = objects[number].selectedData or 1
|
||
|
||
local symbol = "✔"
|
||
local yPos = objects[number].y
|
||
for i = 4, #objects[number] do
|
||
--Коробка для галочки
|
||
ECSAPI.border(x + 1, yPos, 5, 3, background, usualColor)
|
||
--Текст
|
||
gpu.set(x + 7, yPos + 1, objects[number][i])
|
||
--Галочка
|
||
if objects[number].selectedData == (i - 3) then
|
||
ECSAPI.colorText(x + 3, yPos + 1, selectionColor, symbol)
|
||
else
|
||
gpu.set(x + 3, yPos + 1, " ")
|
||
end
|
||
|
||
obj["Selects"] = obj["Selects"] or {}
|
||
obj["Selects"][number] = obj["Selects"][number] or {}
|
||
obj["Selects"][number][i - 3] = { x + 1, yPos, x + width - 2, yPos + 2 }
|
||
|
||
yPos = yPos + objectsHeights.select
|
||
end
|
||
|
||
elseif objectType == "selector" then
|
||
local borderColor = objects[number][2]
|
||
local arrowColor = objects[number][3]
|
||
local selectorWidth = width - 2
|
||
objects[number].selectedElement = objects[number].selectedElement or objects[number][4]
|
||
|
||
local topLine = "┌" .. string.rep("─", selectorWidth - 6) .. "┬───┐"
|
||
local midLine = "│" .. string.rep(" ", selectorWidth - 6) .. "│ │"
|
||
local botLine = "└" .. string.rep("─", selectorWidth - 6) .. "┴───┘"
|
||
|
||
local yPos = objects[number].y
|
||
|
||
local function bordak(borderColor)
|
||
gpu.setBackground(background)
|
||
gpu.setForeground(borderColor)
|
||
gpu.set(x + 1, objects[number].y, topLine)
|
||
gpu.set(x + 1, objects[number].y + 1, midLine)
|
||
gpu.set(x + 1, objects[number].y + 2, botLine)
|
||
gpu.set(x + 3, objects[number].y + 1, ECSAPI.stringLimit("start", objects[number].selectedElement, width - 6))
|
||
ECSAPI.colorText(x + width - 4, objects[number].y + 1, arrowColor, "▼")
|
||
end
|
||
|
||
bordak(borderColor)
|
||
|
||
--Выпадающий список, самый гемор, блядь
|
||
if active then
|
||
local xPos, yPos = x + 1, objects[number].y + 3
|
||
local spisokWidth = width - 2
|
||
local countOfElements = #objects[number] - 3
|
||
local spisokHeight = countOfElements + 1
|
||
local oldPixels = ECSAPI.rememberOldPixels( xPos, yPos, xPos + spisokWidth - 1, yPos + spisokHeight - 1)
|
||
|
||
local coords = {}
|
||
|
||
bordak(arrowColor)
|
||
|
||
--Рамку рисуем поверх фоника
|
||
local topLine = "├"..string.rep("─", spisokWidth - 6).."┴───┤"
|
||
local midLine = "│"..string.rep(" ", spisokWidth - 2).."│"
|
||
local botLine = "└"..string.rep("─", selectorWidth - 2) .. "┘"
|
||
ECSAPI.colorTextWithBack(xPos, yPos - 1, arrowColor, background, topLine)
|
||
for i = 1, spisokHeight - 1 do
|
||
gpu.set(xPos, yPos + i - 1, midLine)
|
||
end
|
||
gpu.set(xPos, yPos + spisokHeight - 1, botLine)
|
||
|
||
--Элементы рисуем
|
||
xPos = xPos + 2
|
||
for i = 1, countOfElements do
|
||
ECSAPI.colorText(xPos, yPos, borderColor, ECSAPI.stringLimit("start", objects[number][i + 3], spisokWidth - 4))
|
||
coords[i] = {xPos - 1, yPos, xPos + spisokWidth - 4, yPos}
|
||
yPos = yPos + 1
|
||
end
|
||
|
||
--Обработка
|
||
local exit
|
||
while true do
|
||
if exit then break end
|
||
local e = {event.pull()}
|
||
if e[1] == "touch" then
|
||
for i = 1, #coords do
|
||
if ECSAPI.clickedAtArea(e[3], e[4], coords[i][1], coords[i][2], coords[i][3], coords[i][4]) then
|
||
ECSAPI.square(coords[i][1], coords[i][2], spisokWidth - 2, 1, ECSAPI.colors.blue)
|
||
ECSAPI.colorText(coords[i][1] + 1, coords[i][2], 0xffffff, objects[number][i + 3])
|
||
os.sleep(0.3)
|
||
objects[number].selectedElement = objects[number][i + 3]
|
||
exit = true
|
||
break
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
ECSAPI.drawOldPixels(oldPixels)
|
||
end
|
||
|
||
newObj("Selectors", number, x + 1, objects[number].y, x + width - 2, objects[number].y + 2)
|
||
|
||
elseif objectType == "separator" then
|
||
ECSAPI.separator(x, objects[number].y, width, background, objects[number][2])
|
||
|
||
elseif objectType == "textfield" then
|
||
newObj("TextFields", number, x + 1, objects[number].y, x + width - 2, objects[number].y + objects[number][2] - 1)
|
||
if not objects[number].strings then objects[number].strings = ECSAPI.stringWrap({objects[number][7]}, width - 3) end
|
||
objects[number].displayFrom = objects[number].displayFrom or 1
|
||
ECSAPI.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])
|
||
|
||
elseif objectType == "wrappedtext" then
|
||
gpu.setBackground(background)
|
||
gpu.setForeground(objects[number][2])
|
||
for i = 1, #objects[number].wrapped do
|
||
gpu.set(x + 2, objects[number].y + i - 1, objects[number].wrapped[i])
|
||
end
|
||
|
||
elseif objectType == "button" then
|
||
|
||
obj["MultiButtons"] = obj["MultiButtons"] or {}
|
||
obj["MultiButtons"][number] = {}
|
||
|
||
local widthOfButton = math.floor(width / (#objects[number] - 1))
|
||
|
||
local xPos, yPos = x, objects[number].y
|
||
for i = 1, #objects[number] do
|
||
if type(objects[number][i]) == "table" then
|
||
local x1, y1, x2, y2 = ECSAPI.drawButton(xPos, yPos, widthOfButton, 3, objects[number][i][3], objects[number][i][1], objects[number][i][2])
|
||
table.insert(obj["MultiButtons"][number], {x1, y1, x2, y2, widthOfButton})
|
||
xPos = x2 + 1
|
||
|
||
if i == #objects[number] then
|
||
ECSAPI.square(xPos, yPos, x + width - xPos, 3, objects[number][i][1])
|
||
obj["MultiButtons"][number][i - 1][5] = obj["MultiButtons"][number][i - 1][5] + x + width - xPos
|
||
end
|
||
|
||
x1, y1, x2, y2 = nil, nil, nil, nil
|
||
end
|
||
end
|
||
|
||
elseif objectType == "switch" then
|
||
|
||
local xPos, yPos = x + 2, objects[number].y
|
||
local activeColor, passiveColor, textColor, text, state = objects[number][2], objects[number][3], objects[number][4], objects[number][5], objects[number][6]
|
||
local switchWidth = 8
|
||
ECSAPI.colorTextWithBack(xPos, yPos, textColor, background, text)
|
||
|
||
xPos = x + width - switchWidth - 2
|
||
if state then
|
||
ECSAPI.square(xPos, yPos, switchWidth, 1, activeColor)
|
||
ECSAPI.square(xPos + switchWidth - 2, yPos, 2, 1, passiveColor)
|
||
--ECSAPI.colorTextWithBack(xPos + 4, yPos, passiveColor, activeColor, "ON")
|
||
else
|
||
ECSAPI.square(xPos, yPos, switchWidth, 1, passiveColor - 0x444444)
|
||
ECSAPI.square(xPos, yPos, 2, 1, passiveColor)
|
||
--ECSAPI.colorTextWithBack(xPos + 4, yPos, passiveColor, passiveColor - 0x444444, "OFF")
|
||
end
|
||
newObj("Switches", number, xPos, yPos, xPos + switchWidth - 1, yPos)
|
||
|
||
elseif objectType == "color" then
|
||
local xPos, yPos = x + 1, objects[number].y
|
||
local blendedColor = require("colorlib").alphaBlend(objects[number][3], 0xFFFFFF, 180)
|
||
local w = width - 2
|
||
|
||
ECSAPI.colorTextWithBack(xPos, yPos + 2, blendedColor, background, string.rep("▀", w))
|
||
ECSAPI.colorText(xPos, yPos, objects[number][3], string.rep("▄", w))
|
||
ECSAPI.square(xPos, yPos + 1, w, 1, objects[number][3])
|
||
|
||
ECSAPI.colorText(xPos + 1, yPos + 1, 0xffffff - objects[number][3], objects[number][2])
|
||
newObj("Colors", number, xPos, yPos, x + width - 2, yPos + 2)
|
||
end
|
||
end
|
||
|
||
--Отображение всех объектов
|
||
local function displayAllObjects()
|
||
for i = 1, countOfObjects do
|
||
displayObject(i)
|
||
end
|
||
end
|
||
|
||
--Подготовить массив возвращаемый
|
||
local function getReturn()
|
||
local massiv = {}
|
||
|
||
for i = 1, countOfObjects do
|
||
local type = string.lower(objects[i][1])
|
||
|
||
if type == "button" then
|
||
table.insert(massiv, pressedButton)
|
||
elseif type == "input" then
|
||
table.insert(massiv, objects[i][4])
|
||
elseif type == "select" then
|
||
table.insert(massiv, objects[i][objects[i].selectedData + 3])
|
||
elseif type == "selector" then
|
||
table.insert(massiv, objects[i].selectedElement)
|
||
elseif type == "slider" then
|
||
table.insert(massiv, objects[i][6])
|
||
elseif type == "switch" then
|
||
table.insert(massiv, objects[i][6])
|
||
elseif type == "color" then
|
||
table.insert(massiv, objects[i][3])
|
||
else
|
||
table.insert(massiv, nil)
|
||
end
|
||
end
|
||
|
||
return massiv
|
||
end
|
||
|
||
local function redrawBeforeClose()
|
||
if closeWindowAfter then
|
||
ECSAPI.drawOldPixels(oldPixels)
|
||
gpu.setBackground(oldBackground)
|
||
gpu.setForeground(oldForeground)
|
||
end
|
||
end
|
||
|
||
--Рисуем окно
|
||
ECSAPI.square(x, y, width, height, background)
|
||
displayAllObjects()
|
||
|
||
while true do
|
||
local e = {event.pull()}
|
||
if e[1] == "touch" or e[1] == "drag" then
|
||
|
||
--Анализируем клик на кнопки
|
||
if obj["MultiButtons"] then
|
||
for key in pairs(obj["MultiButtons"]) do
|
||
for i = 1, #obj["MultiButtons"][key] do
|
||
if ECSAPI.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
|
||
ECSAPI.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])
|
||
os.sleep(0.3)
|
||
pressedButton = objects[key][i + 1][3]
|
||
redrawBeforeClose()
|
||
return getReturn()
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
--А теперь клик на инпуты!
|
||
if obj["Inputs"] then
|
||
for key in pairs(obj["Inputs"]) do
|
||
if ECSAPI.clickedAtArea(e[3], e[4], obj["Inputs"][key][1], obj["Inputs"][key][2], obj["Inputs"][key][3], obj["Inputs"][key][4]) then
|
||
displayObject(key, true)
|
||
displayObject(key)
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
--А теперь галочковыбор!
|
||
if obj["Selects"] then
|
||
for key in pairs(obj["Selects"]) do
|
||
for i in pairs(obj["Selects"][key]) do
|
||
if ECSAPI.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
|
||
objects[key].selectedData = i
|
||
displayObject(key)
|
||
break
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
--Хм, а вот и селектор подъехал!
|
||
if obj["Selectors"] then
|
||
for key in pairs(obj["Selectors"]) do
|
||
if ECSAPI.clickedAtArea(e[3], e[4], obj["Selectors"][key][1], obj["Selectors"][key][2], obj["Selectors"][key][3], obj["Selectors"][key][4]) then
|
||
displayObject(key, true)
|
||
displayObject(key)
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
--Слайдеры, епта! "Потный матан", все делы
|
||
if obj["Sliders"] then
|
||
for key in pairs(obj["Sliders"]) do
|
||
if ECSAPI.clickedAtArea(e[3], e[4], obj["Sliders"][key][1], obj["Sliders"][key][2], obj["Sliders"][key][3], obj["Sliders"][key][4]) then
|
||
local xOfSlider, dolya = obj["Sliders"][key][1], obj["Sliders"][key][5]
|
||
local currentPixels = e[3] - xOfSlider
|
||
local currentValue = math.floor(currentPixels / dolya)
|
||
--Костыль
|
||
if e[3] == obj["Sliders"][key][3] then currentValue = objects[key][5] end
|
||
objects[key][6] = currentValue or objects[key][6]
|
||
displayObject(key)
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
if obj["Switches"] then
|
||
for key in pairs(obj["Switches"]) do
|
||
if ECSAPI.clickedAtArea(e[3], e[4], obj["Switches"][key][1], obj["Switches"][key][2], obj["Switches"][key][3], obj["Switches"][key][4]) then
|
||
objects[key][6] = not objects[key][6]
|
||
displayObject(key)
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
if obj["Colors"] then
|
||
for key in pairs(obj["Colors"]) do
|
||
if ECSAPI.clickedAtArea(e[3], e[4], obj["Colors"][key][1], obj["Colors"][key][2], obj["Colors"][key][3], obj["Colors"][key][4]) then
|
||
local oldColor = objects[key][3]
|
||
objects[key][3] = 0xffffff - objects[key][3]
|
||
displayObject(key)
|
||
os.sleep(0.3)
|
||
objects[key][3] = oldColor
|
||
displayObject(key)
|
||
local color = loadfile("lib/palette.lua")().draw("auto", "auto", objects[key][3])
|
||
objects[key][3] = color or oldColor
|
||
displayObject(key)
|
||
break
|
||
end
|
||
end
|
||
end
|
||
|
||
elseif e[1] == "scroll" then
|
||
if obj["TextFields"] then
|
||
for key in pairs(obj["TextFields"]) do
|
||
if ECSAPI.clickedAtArea(e[3], e[4], obj["TextFields"][key][1], obj["TextFields"][key][2], obj["TextFields"][key][3], obj["TextFields"][key][4]) then
|
||
if e[5] == 1 then
|
||
if objects[key].displayFrom > 1 then objects[key].displayFrom = objects[key].displayFrom - 1; displayObject(key) end
|
||
else
|
||
if objects[key].displayFrom < #objects[key].strings then objects[key].displayFrom = objects[key].displayFrom + 1; displayObject(key) end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
elseif e[1] == "key_down" then
|
||
if e[4] == 28 then
|
||
redrawBeforeClose()
|
||
return getReturn()
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
--Демонстрационное окно, показывающее всю мощь universalWindow
|
||
function ECSAPI.demoWindow()
|
||
--Очищаем экран перед юзанием окна и ставим курсор на 1, 1
|
||
ECSAPI.prepareToExit()
|
||
--Рисуем окно и получаем данные после взаимодействия с ним
|
||
local data = ECSAPI.universalWindow("auto", "auto", 36, 0xeeeeee, true,
|
||
{"EmptyLine"},
|
||
{"CenterText", 0x880000, "Здорово, ебана!"},
|
||
{"EmptyLine"},
|
||
{"Input", 0x262626, 0x880000, "Сюда вводить можно"},
|
||
{"Selector", 0x262626, 0x880000, "Выбор формата", "PNG", "JPG", "GIF", "PSD"},
|
||
{"EmptyLine"},
|
||
{"WrappedText", 0x262626, "Тест автоматического переноса букв в зависимости от ширины данного окна. Пока что тупо режет на куски, не особо красиво."},
|
||
{"EmptyLine"},
|
||
{"Select", 0x262626, 0x880000, "Я пидор", "Я не пидор"},
|
||
{"Slider", 0x262626, 0x880000, 1, 100, 50, "Убито ", " младенцев"},
|
||
{"EmptyLine"},
|
||
{"Separator", 0xaaaaaa},
|
||
{"Switch", 0xF2B233, 0xffffff, 0x262626, "✈ Авиарежим", false},
|
||
{"EmptyLine"},
|
||
{"Switch", 0x3366CC, 0xffffff, 0x262626, "☾ Не беспокоить", true},
|
||
{"Separator", 0xaaaaaa},
|
||
{"EmptyLine"},
|
||
{"TextField", 5, 0xffffff, 0x262626, 0xcccccc, 0x3366CC, "Тест текстового информационного поля. По сути это тот же самый WrappedText, разве что эта хрень ограничена по высоте, и ее можно скроллить. Ну же, поскролль меня! Скролль меня полностью! Моя жадная пизда жаждет твой хуй!"},
|
||
{"Color", "Цвет фона", 0xFF0000},
|
||
{"EmptyLine"},
|
||
{"Button", {0x57A64E, 0xffffff, "Да"}, {0xF2B233, 0xffffff, "Нет"}, {0xCC4C4C, 0xffffff, "Отмена"}}
|
||
)
|
||
--Еще разок
|
||
ECSAPI.prepareToExit()
|
||
--Выводим данные
|
||
print(" ")
|
||
print("Вывод данных из окна:")
|
||
for i = 1, #data do print("["..i.."] = "..tostring(data[i])) end
|
||
print(" ")
|
||
end
|
||
|
||
-- ECSAPI.demoWindow()
|
||
|
||
--[[
|
||
Функция universalWindow(x, y, width, background, closeWindowAfter, ...)
|
||
|
||
Это универсальная модульная функция для максимально удобного и быстрого отображения
|
||
необходимой вам информации. С ее помощью вводить данные с клавиатуры, осуществлять выбор
|
||
из предложенных вариантов, рисовать красивые кнопки, отрисовывать обычный текст,
|
||
отрисовывать текстовые поля с возможностью прокрутки, рисовать разделители и прочее.
|
||
Любой объект выделяется с помощью клика мыши, после чего функция приступает к работе
|
||
с этим объектом.
|
||
|
||
Аргументы функции:
|
||
|
||
x и y: это числа, обозначающие стартовые координаты левого верхнего угла данного окна.
|
||
Вместо цифр вы также можете написать "auto" - и программа автоматически разместит окно
|
||
по центру экрана по выбранной координате. Или по обеим координатам, если вам угодно.
|
||
|
||
width: это ширина окна, которую вы можете задать по собственному желанию. Если некторые
|
||
объекты требуют расширения окна, то окно будет автоматически расширено до нужной ширины.
|
||
Да, вот такая вот тавтология ;)
|
||
|
||
background: базовый цвет окна (цвет фона, кому как понятнее).
|
||
|
||
closeWindowAfter: eсли true, то окно по завершению функции будет выгружено, а на его месте
|
||
отрисуются пиксели, которые имелись на экране до выполнения функции. Удобно, если не хочешь
|
||
париться с перерисовкой интерфейса.
|
||
|
||
... : многоточием тут является перечень объектов, указанных через запятую. Каждый объект
|
||
является массивом и имеет собственный формат. Ниже перечислены все возможные типы объектов.
|
||
|
||
{"Button", {Цвет кнопки1, Цвет текста на кнопке1, Сам текст1}, {Цвет кнопки2, Цвет текста на кнопке2, Сам текст2}, ...}
|
||
|
||
Это объект для рисования кнопок. Каждая кнопка - это массив, состоящий из трех элементов:
|
||
цвета кнопки, цвета текста на кнопке и самого текста. Кнопок может быть неограниченное количество,
|
||
однако чем их больше, тем большее требуется разрешение экрана по ширине.
|
||
|
||
Интерактивный объект.
|
||
|
||
{"Input", Цвет рамки и текста, Цвет при выделении, Стартовый текст [, Маскировать символом]}
|
||
|
||
Объект для рисования полей ввода текстовой информации. Удобно для открытия или сохранения файлов,
|
||
Опциональный аргумент "Маскировать символом" полезен, если вы делаете поле для ввода пароля.
|
||
Никто не увидит ваш текст. В качестве данного аргумента передается символ, например "*".
|
||
|
||
Интерактивный объект.
|
||
|
||
{"Selector", Цвет рамки, Цвет при выделении, Выбор 1, Выбор 2, Выбор 3 ...}
|
||
|
||
Внешне схож с объектом "Input", однако в этом случае вы будете выбирать один из предложенных
|
||
вариантов из выпадающего списка. По умолчанию выбран первый вариант.
|
||
|
||
Интерактивный объект.
|
||
|
||
{"Select", Цвет рамки, Цвет галочки, Выбор 1, Выбор 2, Выбор 3 ...}
|
||
|
||
Объект выбора. Отличается от "Selector" тем, что здесь вы выбираете один из вариантов, отмечая
|
||
его галочкой. По умолчанию выбран первый вариант.
|
||
|
||
Интерактивный объект.
|
||
|
||
{"Slider", Цвет линии слайдера, Цвет пимпочки слайдера, Значения слайдера ОТ, Значения слайдера ДО, Текущее значение [, Текст-подсказка ДО] [, Текст-подсказка ПОСЛЕ]}
|
||
|
||
Ползунок, позволяющий задавать определенное количество чего-либо в указанном интервале. Имеются два
|
||
опциональных аргумента, позволяющих четко понимать, с чем именно мы имеем дело.
|
||
|
||
К примеру, если аргумент "Текст-подсказка ДО" будет равен "Съедено ", а аргумент "Текст-подсказка ПОСЛЕ"
|
||
будет равен " яблок", а значение слайдера будет равно 50, то на экране будет написано "Съедено 50 яблок".
|
||
|
||
Интерактивный объект.
|
||
|
||
{"Switch", Активный цвет, Пассивный цвет, Цвет текста, Текст, Состояние}
|
||
|
||
Переключатель, принимающий два состояния: true или false. Текст - это всего лишь информация, некое
|
||
название данного переключателя.
|
||
|
||
Интерактивный объект.
|
||
|
||
{"CenterText", Цвет текста, Сам текст}
|
||
|
||
Отображение текста указанного цвета по центру окна. Чисто для информативных целей.
|
||
|
||
{"WrappedText", Цвет текста, Текст}
|
||
|
||
Отображение большого количества текста с автоматическим переносом. Прото режет слова на кусочки,
|
||
перенос символический. Чисто для информативных целей.
|
||
|
||
{"TextField", Высота, Цвет фона, Цвет текста, Цвет скроллбара, Цвет пимпочки скроллбара, Сам текст}
|
||
|
||
Текстовое поле с возможностью прокрутки. Отличается от "WrappedText"
|
||
фиксированной высотой. Чисто для информативных целей.
|
||
|
||
{"Separator", Цвет разделителя}
|
||
|
||
Линия-разделитель, помогающая лучше отделять объекты друг от друга. Декоративный объект.
|
||
|
||
{"EmptyLine"}
|
||
|
||
Пустое пространство, помогающая лучше отделять объекты друг от друга. Декоративный объект.
|
||
|
||
Каждый из объектов рисуется по порядку сверху вниз. Каждый объект автоматически
|
||
увеличивает высоту окна до необходимого значения. Если объектов будет указано слишком много -
|
||
т.е. если окно вылезет за пределы экрана, то программа завершится с ошибкой.
|
||
|
||
Что возвращает функция:
|
||
|
||
Возвратом является массив, пронумерованный от 1 до <количества объектов>.
|
||
К примеру, 1 индекс данного массива соответствует 1 указанному объекту.
|
||
Каждый индекс данного массива несет в себе какие-то данные, которые вы
|
||
внесли в объект во время работы функции.
|
||
Например, если в 1-ый объект типа "Input" вы ввели фразу "Hello world",
|
||
то первый индекс в возвращенном массиве будет равен "Hello world".
|
||
Конкретнее это будет вот так: massiv[1] = "Hello world".
|
||
|
||
Если взаимодействие с объектом невозможно - например, как в случае
|
||
с EmptyLine, CenterText, TextField или Separator, то в возвращенном
|
||
массиве этот объект указываться не будет.
|
||
|
||
Готовые примеры использования функции указаны ниже и закомментированы.
|
||
Выбирайте нужный и раскомментируйте.
|
||
]]
|
||
|
||
--Функция-демонстратор, показывающая все возможные объекты в одном окне. Код окна находится выше.
|
||
--ECSAPI.demoWindow()
|
||
|
||
--Функция-отладчик, выдающая окно с указанным сообщением об ошибке. Полезна при дебаге.
|
||
--ECSAPI.error("Это сообщение об ошибке! Hello world!")
|
||
|
||
--Функция, спрашивающая, стоит ли заменять указанный файл, если он уже имеется
|
||
--ECSAPI.askForReplaceFile("OS.lua")
|
||
|
||
--Функция, предлагающая сохранить файл в нужном месте в нужном формате.
|
||
--ECSAPI.universalWindow("auto", "auto", 30, ECSAPI.windowColors.background, true, {"EmptyLine"}, {"CenterText", 0x262626, "Сохранить как"}, {"EmptyLine"}, {"Input", 0x262626, 0x880000, "Путь"}, {"Selector", 0x262626, 0x880000, "PNG", "JPG", "PSD"}, {"EmptyLine"}, {"Button", {0xbbbbbb, 0xffffff, "OK!"}})
|
||
|
||
|
||
----------------------------------------------------------------------------------------------------
|
||
|
||
return ECSAPI
|
||
|
||
|