diff --git a/Applications/VK/VK.lua b/Applications/VK/VK.lua index cf8aa9fa..2660614a 100644 --- a/Applications/VK/VK.lua +++ b/Applications/VK/VK.lua @@ -13,6 +13,7 @@ local unicode = require("unicode") local component = require("component") local computer = require("computer") local files = require("files") +local GUI = require("GUI") ---------------------------------------------------- Константы ---------------------------------------------------------------- @@ -355,7 +356,7 @@ local function loginGUI(startUsername, startPassword) loginData.password = password return loginData else - buffer.error("Ошибка авторизации: " .. tostring(loginData)) + GUI.error("Ошибка авторизации: " .. tostring(loginData)) end end end @@ -662,7 +663,6 @@ local function audioGUI(ID) if i % 2 == 0 then color = 0xEEEEEE end buffer.square(mainZoneX, y, mainZoneWidth, 5, color) - -- buffer.button(mainZoneX + 2, y + 1, 5, 3, colors.audioPlayButton, colors.audioPlayButtonText, "ᐅ") buffer.button(mainZoneX + 2, y + 1, 5, 3, colors.audioPlayButton, colors.audioPlayButtonText, ">") newObj("audio", i, mainZoneX + 2, y + 1, mainZoneX + 7, y + 3, audios.response.items[i]) @@ -680,7 +680,7 @@ local function audioGUI(ID) y = y + 5 end else - buffer.error("Ошибка при получении списка аудиозаписей") + GUI.error("Ошибка при получении списка аудиозаписей") end end @@ -720,7 +720,7 @@ local function userProfileRequest() currentProfile.friends = friends return true else - buffer.error("Ошибка при загрузке информации о профиле") + GUI.error("Ошибка при загрузке информации о профиле") return false end end @@ -802,7 +802,7 @@ local function userProfileGUI() for i = 1, #currentProfile.wall.response.items do --Если это не репост или еще не хуйня какая-то if currentProfile.wall.response.items[i].text ~= "" then - -- buffer.error(userNames) + -- GUI.error(userNames) drawAvatar(x, y, 6, 3, currentProfile.wall.response.items[i].from_id, unicode.sub(currentProfile.userNames[currentProfile.wall.response.items[i].from_id].first_name, 1, 1) .. unicode.sub(currentProfile.userNames[currentProfile.wall.response.items[i].from_id].last_name, 1, 1)) buffer.text(x + 8, y, informationValueColor, currentProfile.userNames[currentProfile.wall.response.items[i].from_id].first_name .. " " .. currentProfile.userNames[currentProfile.wall.response.items[i].from_id].last_name) local date = os.date("%d.%m.%y в %H:%M", currentProfile.wall.response.items[i].date) @@ -921,7 +921,7 @@ local function friendsGUI() buffer.resetDrawLimit() else - buffer.error("Ошибка при получении списка друзей пользователя") + GUI.error("Ошибка при получении списка друзей пользователя") end end @@ -998,7 +998,7 @@ local function getAndShowNews() currentNews = 1 newsGUI() else - buffer.error("Ошибка при получении списка новостей") + GUI.error("Ошибка при получении списка новостей") end end @@ -1120,7 +1120,7 @@ while true do status("Вывожу в статус играемую музыку") setCurrentAudioPlaying(currentProfile and currentProfile.ID or personalInfo.id, obj.audio[key][5].id) else - buffer.error("Эта функция доступна только при наличии установленного мода OpenFM, добавляющего полноценное интернет-радио") + GUI.error("Эта функция доступна только при наличии установленного мода OpenFM, добавляющего полноценное интернет-радио") end break @@ -1157,7 +1157,7 @@ while true do for i = 1, data[2] do local count = 1 for key in pairs(obj.dialogList) do - -- buffer.error("Ебашу спам диалогу под пиром: " .. obj.dialogList[key][5]) + -- GUI.error("Ебашу спам диалогу под пиром: " .. obj.dialogList[key][5]) ecs.info("auto", "auto", "CrazyTyping", "Запрос: " .. i .. " из " .. data[2] .. ", диалог: " .. count .. " из ".. data[1] .. ", peerID: " .. obj.dialogList[key][5]) setCrazyTypingRequest(obj.dialogList[key][5]) count = count + 1 @@ -1231,7 +1231,7 @@ while true do for key in pairs(obj.leftBar) do if clickedAtZone(e[3], e[4], obj.leftBar[key]) then - -- buffer.error("Кликнули на лефт бар ээлемент") + -- GUI.error("Кликнули на лефт бар ээлемент") local oldLeftBarElement = currentLeftBarElement currentLeftBarElement = key mainGUI() diff --git a/Applications/Weather/Weather.lua b/Applications/Weather/Weather.lua index 492de7bf..c07e866b 100644 --- a/Applications/Weather/Weather.lua +++ b/Applications/Weather/Weather.lua @@ -12,6 +12,7 @@ local image = require("image") local unicode = require("unicode") local files = require("files") local component = require("component") +local GUI = require("GUI") ---------------------------------------------------- Константы ---------------------------------------------------------------- @@ -266,7 +267,7 @@ local function tryToGetAndDrawWeather() --Сейвим погодку saveWeatherData() else - buffer.error(jsonWeatherResponse) + GUI.error(jsonWeatherResponse, {title = {color = 0xFF8888, text = "Ошибка"}}) end end diff --git a/lib/ECSAPI.lua b/lib/ECSAPI.lua index 6b6eb604..158267ca 100644 --- a/lib/ECSAPI.lua +++ b/lib/ECSAPI.lua @@ -1001,53 +1001,70 @@ function ecs.emptyWindow(x,y,width,height,title) end +function ecs.getWordsArrayFromString(s) + local words = {} + for word in string.gmatch(s, "[^%s]+") do table.insert(words, word) end + return words +end + --Функция по переносу слов на новую строку в зависимости от ограничения по ширине function ecs.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 + local currentString = 1 + while currentString <= #strings do + local words = ecs.getWordsArrayFromString(strings[currentString]) - i = i + 1 + local newStringThatFormedFromWords, oldStringThatFormedFromWords = "", "" + local word = 1 + local overflow = false + while word <= #words do + oldStringThatFormedFromWords = oldStringThatFormedFromWords .. (word > 1 and " " or "") .. words[word] + if unicode.len(oldStringThatFormedFromWords) > limit then + --ЕБЛО + if unicode.len(words[word]) > limit then + local left = unicode.sub(oldStringThatFormedFromWords, 1, limit) + local right = unicode.sub(strings[currentString], unicode.len(left) + 1, -1) + overflow = true + strings[currentString] = left + if strings[currentString + 1] then + strings[currentString + 1] = right .. " " .. strings[currentString + 1] + else + strings[currentString + 1] = right + end + end + break + else + newStringThatFormedFromWords = oldStringThatFormedFromWords + end + word = word + 1 + end - -- --Создаем массив слов данной строки - -- local words = {} - -- for match in string.gmatch(strings[i], "[^%s]+") do table.insert(words, match) end + if word <= #words and not overflow then + local fuckToAdd = table.concat(words, " ", word, #words) + if strings[currentString + 1] then + strings[currentString + 1] = fuckToAdd .. " " .. strings[currentString + 1] + else + strings[currentString + 1] = fuckToAdd + end + strings[currentString] = newStringThatFormedFromWords + 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 + currentString = currentString + 1 + end - -- to = to + 1 - -- end - -- end - end - - return strings + return strings + -- 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 + -- end + -- return strings end --Моя любимая функция ошибки C: diff --git a/lib/GUI.lua b/lib/GUI.lua new file mode 100644 index 00000000..c93e3d22 --- /dev/null +++ b/lib/GUI.lua @@ -0,0 +1,237 @@ + +if not _G.buffer and not package.loaded.doubleBuffering then _G.buffer = require("doubleBuffering") end +if not _G.ecs and not package.loaded.ECSAPI then _G.ecs = require("ECSAPI") end +if not _G.unicode and not package.loaded.unicode then _G.unicode = require("unicode") end +local GUI = {} + +---------------------------------------------------- Универсальные методы -------------------------------------------------------- + +GUI.directions = { + horizontal = 0, + vertical = 1, +} + +GUI.buttonTypes = { + default = 0, + adaptive = 1, +} + +-- Универсальный метод для проверки клика на прямоугольный объект +local function clickedAtObject(object, x, y) + if x >= object.x and y >= object.y and x <= object.x + object.width - 1 and y <= object.y + object.height - 1 then return true end + return false +end + +---------------------------------------------------- Кнопки -------------------------------------------------------------------- + +-- Метод-рисоватор кнопки +local function drawButton(buttonObject, isPressed) + local textLength = unicode.len(buttonObject.text) + if textLength > buttonObject.width then buttonObject.text = unicode.sub(buttonObject.text, 1, buttonObject.width) end + + local xText = math.floor(buttonObject.x + buttonObject.width / 2 - textLength / 2) + local yText = math.floor(buttonObject.y + buttonObject.height / 2) + local buttonColor = isPressed and buttonObject.colors.pressed.button or buttonObject.colors.default.button + local textColor = isPressed and buttonObject.colors.pressed.text or buttonObject.colors.default.text + + buffer.square(buttonObject.x, buttonObject.y, buttonObject.width, buttonObject.height, buttonColor, textColor, " ") + buffer.text(xText, yText, textColor, buttonObject.text) +end + +-- Метод-нажиматор кнопки +local function pressButton(buttonObject, pressTime) + drawButton(buttonObject, true) + buffer.draw() + os.sleep(pressTime or 0.2) + drawButton(buttonObject, false) + buffer.draw() +end + +-- Создание таблицы кнопки со всеми необходимыми параметрами +local function createButtonObject(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text) + return { + x = x, + y = y, + width = width, + height = height, + colors = { + default = { + button = buttonColor, + text = textColor + }, + pressed = { + button = buttonPressedColor, + text = textPressedColor + }, + }, + text = text, + press = pressButton, + isClicked = clickedAtObject, + draw = drawButton, + } +end + +-- Кнопка фиксированных размеров +function GUI.button(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text) + local buttonObject = createButtonObject(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text) + buttonObject:draw() + return buttonObject +end + +-- Кнопка, подстраивающаяся под длину текста +function GUI.adaptiveButton(x, y, xOffset, yOffset, buttonColor, textColor, buttonPressedColor, textPressedColor, text) + local buttonObject = createButtonObject(x, y, xOffset * 2 + unicode.len(text), yOffset * 2 + 1, buttonColor, textColor, buttonPressedColor, textPressedColor, text) + buttonObject:draw() + return buttonObject +end + +-- Вертикальный или горизонтальный ряд кнопок +-- Каждая кнопка - это массив вида {enum GUI.buttonTypes.default или GUI.buttonTypes.adaptive, int ширина/отступ, int высота/отступ, int цвет кнопки, int цвет текста, int цвет нажатой кнопки, int цвет нажатого текста, string текст} +-- Метод возвращает обычный массив кнопочных объектов (см. выше) +function GUI.buttons(x, y, direction, spaceBetweenButtons, ...) + local buttons = {...} + local buttonObjects = {} + + local function drawCorrectButton(i) + if buttons[i][1] == GUI.buttonTypes.default then + return GUI.button(x, y, buttons[i][2], buttons[i][3], buttons[i][4], buttons[i][5], buttons[i][6], buttons[i][7], buttons[i][8]) + elseif buttons[i][1] == GUI.buttonTypes.adaptive then + return GUI.adaptiveButton(x, y, buttons[i][2], buttons[i][3], buttons[i][4], buttons[i][5], buttons[i][6], buttons[i][7], buttons[i][8]) + else + error("Неподдерживаемый тип кнопки: " .. tostring(buttons[i][1])) + end + end + + for i = 1, #buttons do + buttonObjects[i] = drawCorrectButton(i) + if direction == GUI.directions.horizontal then + x = x + buttonObjects[i].width + spaceBetweenButtons + elseif direction == GUI.directions.vertical then + y = y + buttonObjects[i].height + spaceBetweenButtons + else + error("Неподдерживаемое направление: " .. tostring(buttons[i][1])) + end + end + + return buttonObjects +end + + +-------------------------------------------------------------------------------------------------------------------------------- + +-- Красивое окошко для отображения сообщения об ошибке. Аргумент errorWindowParameters может принимать следующие значения: +-- local errorWindowParameters = { +-- backgroundColor = 0x262626, +-- textColor = 0xFFFFFF, +-- truncate = 50, +-- title = {color = 0xFF8888, text = "Ошибочка"} +-- noAnimation = true, +-- } +function GUI.error(text, errorWindowParameters) + --Всякие константы, бла-бла + local backgroundColor = (errorWindowParameters and errorWindowParameters.backgroundColor) or 0x1b1b1b + local errorPixMap = { + {{0xffdb40 , 0xffffff,"#"}, {0xffdb40 , 0xffffff, "#"}, {backgroundColor, 0xffdb40, "▟"}, {backgroundColor, 0xffdb40, "▙"}, {0xffdb40 , 0xffffff, "#"}, {0xffdb40 , 0xffffff, "#"}}, + {{0xffdb40 , 0xffffff,"#"}, {backgroundColor, 0xffdb40, "▟"}, {0xffdb40 , 0xffffff, " "}, {0xffdb40 , 0xffffff, " "}, {backgroundColor, 0xffdb40, "▙"}, {0xffdb40 , 0xffffff, "#"}}, + {{backgroundColor, 0xffdb40,"▟"}, {0xffdb40 , 0xffffff, "c"}, {0xffdb40 , 0xffffff, "y"}, {0xffdb40 , 0xffffff, "k"}, {0xffdb40 , 0xffffff, "a"}, {backgroundColor, 0xffdb40, "▙"}}, + } + local textColor = (errorWindowParameters and errorWindowParameters.textColor) or 0xFFFFFF + local buttonWidth = 12 + local verticalOffset = 2 + local minimumHeight = verticalOffset * 2 + #errorPixMap + local height = 0 + local widthOfText = math.floor(buffer.screen.width * 0.5) + + --Ебемся с текстом, делаем его пиздатым во всех смыслах + if type(text) == "table" then text = serialization.serialize(text) end + text = tostring(text) + text = (errorWindowParameters and errorWindowParameters.truncate) and ecs.stringLimit("end", text, errorWindowParameters.truncate) or text + text = { text } + text = ecs.stringWrap(text, widthOfText) + + + --Ебашим высоту правильнуюe + height = verticalOffset * 2 + #text + 1 + if errorWindowParameters and errorWindowParameters.title then height = height + 2 end + if height < minimumHeight then height = minimumHeight end + + --Ебашим стартовые коорды отрисовки + local x, y = math.ceil(buffer.screen.width / 2 - widthOfText / 2), math.ceil(buffer.screen.height / 2 - height / 2) + local OKButton = {} + local oldPixels = buffer.copy(1, y, buffer.screen.width, height) + + --Отрисовочка + local function draw() + local yPos = y + --Подложка + buffer.square(1, yPos, buffer.screen.width, height, backgroundColor, 0x000000); yPos = yPos + verticalOffset + buffer.customImage(x - #errorPixMap[1] - 3, yPos, errorPixMap) + --Титл, епта! + if errorWindowParameters and errorWindowParameters.title then buffer.text(x, yPos, errorWindowParameters.title.color, errorWindowParameters.title.text); yPos = yPos + 2 end + --Текстус + for i = 1, #text do buffer.text(x, yPos, textColor, text[i]); yPos = yPos + 1 end; yPos = yPos + 1 + --Кнопачка + OKButton = GUI.button(x + widthOfText - buttonWidth, y + height - 2, buttonWidth, 1, 0x3392FF, 0xFFFFFF, 0xFFFFFF, 0x262626, "OK") + --Атрисовачка + buffer.draw() + end + + --Графонистый выход + local function quit() + OKButton:press(0.2) + buffer.paste(1, y, oldPixels) + buffer.draw() + end + + --Онимацыя + if not (errorWindowParameters and errorWindowParameters.noAnimation) then for i = 1, height do buffer.setDrawLimit(1, math.floor(buffer.screen.height / 2) - i, buffer.screen.width, i * 2); draw(); os.sleep(0.05) end; buffer.resetDrawLimit() end + draw() + + --Анализ говнища + while true do + local e = {event.pull()} + if e[1] == "key_down" then + if e[4] == 28 then + quit(); return + end + elseif e[1] == "touch" then + if OKButton:isClicked(e[3], e[4]) then + quit(); return + end + end + end +end + +-------------------------------------------------------------------------------------------------------------------------------- + +-- buffer.clear(0xFFAAAA) +-- buffer.draw(true) + +-- GUI.error("Ублюдок, мать твою, а ну иди сюда, говно собачье, а ну решил ко мне лезть, ты... засранец вонючий, мать твою. А?! ну иди сюда, попробуй меня трахнуть, я тебя сам трахну ублюдок, анонист чертов, будь ты проклят, иди идиот, трахать тебя за свою семью, говно собачье, жлоб вонючий, дерьмо, сука, падла, иди сюда мерзавец, негодяй, гад, иди сюда ты говно, жопа!", {title = {color = 0xFF7777, text = "Ошибка авторизации"}}) + +-- local event = require("event") +-- local myButton = GUI.adaptiveButton(2, 2, 2, 1, 0xFFFFFF, 0x000000, 0xFF8888, 0xFFFFFF, "Кнопачка") +-- buffer.draw() +-- while true do +-- local e = {event.pull("touch")} +-- if myButton:isClicked(e[3], e[4]) then +-- myButton:press(0.2) +-- end +-- end + +-- local myButtons = GUI.buttons(2, 2, GUI.directions.horizontal, 2, {GUI.buttonTypes.adaptive, 2, 0, 0xCCCCCC, 0x262626, 0xFF8888, 0xFFFFFF, "Кнопачка1"}, {GUI.buttonTypes.default, 30, 1, 0xCCCCCC, 0x262626, 0xFF8888, 0xFFFFFF, "Кнопачка2"}, {GUI.buttonTypes.adaptive, 2, 0, 0xCCCCCC, 0x262626, 0xFF8888, 0xFFFFFF, "Кнопачка3"}) +-- buffer.draw() +-- while true do +-- local e = {event.pull("touch")} +-- for _, button in pairs(myButtons) do +-- if button:isClicked(e[3], e[4]) then +-- button:press(0.2) +-- end +-- end +-- end + +-------------------------------------------------------------------------------------------------------------------------------- + +return GUI + + diff --git a/lib/doubleBuffering.lua b/lib/doubleBuffering.lua index 7730e7a4..1dca6f82 100644 --- a/lib/doubleBuffering.lua +++ b/lib/doubleBuffering.lua @@ -438,91 +438,6 @@ function buffer.framedButton(x, y, width, height, backColor, buttonColor, text) buffer.text(x, y, buttonColor, text) end -function buffer.error(text, errorWindowParameters) - --Всякие константы, бла-бла - local backgroundColor = (errorWindowParameters and errorWindowParameters.backgroundColor) and errorWindowParameters.backgroundColor or 0x1b1b1b - local textColor = (errorWindowParameters and errorWindowParameters.textColor) and errorWindowParameters.textColor or 0xFFFFFF - local errorPixMap = { - { {0xffdb40,0xffffff,"#"}, {0xffdb40,0xffffff,"#"}, {backgroundColor,0xffdb40,"▟"}, {backgroundColor,0xffdb40,"▙"}, {0xffdb40,0xffffff,"#"}, {0xffdb40,0xffffff,"#"} }, - { {0xffdb40,0xffffff,"#"}, {backgroundColor,0xffdb40,"▟"}, {0xffdb40,0xffffff," "}, {0xffdb40,0xffffff," "}, {backgroundColor,0xffdb40,"▙"}, {0xffdb40,0xffffff,"#"} }, - { {backgroundColor,0xffdb40,"▟"}, {0xffdb40,0xffffff,"c"}, {0xffdb40,0xffffff,"y"}, {0xffdb40,0xffffff,"k"}, {0xffdb40,0xffffff,"a"}, {backgroundColor,0xffdb40,"▙"} }, - } - local buttonWidth = 12 - local verticalOffset = 2 - local minimumHeight = verticalOffset * 2 + #errorPixMap - local height = 0 - local widthOfText = math.floor(buffer.screen.width * 0.5) - - --Ебемся с текстом, делаем его пиздатым во всех смыслах - if type(text) == "table" then text = serialization.serialize(text) end - text = tostring(text) - text = (errorWindowParameters and errorWindowParameters.truncate) and ecs.stringLimit("end", text, errorWindowParameters.truncate) or text - text = { text } - text = ecs.stringWrap(text, widthOfText) - - --Ебашим высоту правильную - height = verticalOffset - height = height + #text - if errorWindowParameters and errorWindowParameters.title then height = height + 2 end - height = height + verticalOffset + 1 - if height < minimumHeight then height = minimumHeight end - - --Ебашим стартовые коорды отрисовки - local x, y = math.ceil(buffer.screen.width / 2 - widthOfText / 2), math.ceil(buffer.screen.height / 2 - height / 2) - local OKButton = {} - local oldPixels = buffer.copy(1, y, buffer.screen.width, height) - - --Отрисовочка - local function draw() - local yPos = y - --Подложка - buffer.square(1, yPos, buffer.screen.width, height, backgroundColor, 0x000000); yPos = yPos + 2 - buffer.customImage(x - #errorPixMap[1] - 3, yPos, errorPixMap) - --Титл, епта! - if errorWindowParameters and errorWindowParameters.title then buffer.text(x, yPos, errorWindowParameters.title.color, errorWindowParameters.title.text); yPos = yPos + 2 end - --Текстус - for i = 1, #text do buffer.text(x, yPos, textColor, text[i]); yPos = yPos + 1 end; yPos = yPos + 1 - --Кнопачка - OKButton = {buffer.button(x + widthOfText - buttonWidth, y + height - 2, buttonWidth, 1, 0x3392FF, 0xFFFFFF, "OK")} - --Атрисовачка - buffer.draw() - end - - --Онимацыя - for i = 1, height do - buffer.setDrawLimit(1, math.floor(buffer.screen.height / 2) - i, buffer.screen.width, i * 2) - draw() - os.sleep(0.05) - end - buffer.resetDrawLimit() - draw() - - --Графонистый выход - local function quit() - buffer.button(x + widthOfText - buttonWidth, y + height - 2, buttonWidth, 1, 0xFFFFFF, 0x3392FF, "OK") - buffer.draw() - os.sleep(0.2) - buffer.paste(1, y, oldPixels) - buffer.draw() - end - - --Анализ говнища - while true do - local e = {event.pull()} - if e[1] == "key_down" then - if e[4] == 28 then - quit() - return - end - elseif e[1] == "touch" then - if ecs.clickedAtArea(e[3], e[4], OKButton[1], OKButton[2], OKButton[3], OKButton[4]) then - quit() - return - end - end - end -end - ------------------------------------------- Просчет изменений и отрисовка ------------------------------------------------------------------------ --Функция рассчитывает изменения и применяет их, возвращая то, что было изменено