diff --git a/Applications.txt b/Applications.txt index 79c5046e..78f9c2c0 100644 --- a/Applications.txt +++ b/Applications.txt @@ -249,7 +249,7 @@ ["name"]="lib/GUI.lua", ["url"]="IgorTimofeev/OpenComputers/master/lib/GUI.lua", ["type"]="Library", - ["version"]=1.0, + ["version"]=1.1, }, { ["name"]="lib/serialization.lua", @@ -351,7 +351,7 @@ ["name"]="lib/doubleBuffering.lua", ["url"]="IgorTimofeev/OpenComputers/master/lib/doubleBuffering.lua", ["type"]="Library", - ["version"]=1.0, + ["version"]=1.1, }, { ["name"]="lib/thread.lua", @@ -578,7 +578,7 @@ ["icon"]="IgorTimofeev/OpenComputers/master/Applications/AppMarket/Icon.pic", ["createShortcut"]="dock", ["forceDownload"]=true, - ["version"]=1.34, + ["version"]=1.35, }, { ["name"]="MineOS/Applications/Snake", diff --git a/Applications/AppMarket/AppMarket.lua b/Applications/AppMarket/AppMarket.lua index 5293a883..ddfd3dc5 100644 --- a/Applications/AppMarket/AppMarket.lua +++ b/Applications/AppMarket/AppMarket.lua @@ -1,4 +1,7 @@ +package.loaded.GUI = nil +_G.GUI = nil + local libraries = { buffer = "doubleBuffering", MineOSCore = "MineOSCore", @@ -48,8 +51,7 @@ local pathToApplications = "MineOS/System/OS/Applications.txt" local updateImage = image.load(MineOSCore.paths.icons .. "Update.pic") -- local topBarElements = {{title = "Приложения", type = "Application"}, {title = "Библиотеки", type = "Library"}, {title = "Обои", type = "Wallpaper"}, {title = "Другое"}, {title = "Обновления"}} local topBarElements = {"Приложения", "Библиотеки", "Обои", "Другое", "Обновления"} -local oldApplications, newApplications, changes = {}, {}, {} -local currentApps = {} +local oldApplications, newApplications, currentApps, changes = {}, {}, {}, {} local currentTopBarElement = 1 local from, limit, fromY = 1, 8 @@ -72,10 +74,11 @@ local function calculateSizes() sizes.width, sizes.height = math.floor(buffer.screen.width * 0.6), math.floor(buffer.screen.height * 0.7) sizes.x, sizes.y = math.floor(buffer.screen.width / 2 - sizes.width / 2), math.floor(buffer.screen.height / 2 - sizes.height / 2) sizes.topBarHeight = 3 - sizes.yMain = sizes.y + sizes.topBarHeight - sizes.mainHeight = sizes.height - sizes.topBarHeight + obj.main = GUI.object(sizes.x, sizes.y + sizes.topBarHeight, sizes.width, sizes.height - sizes.topBarHeight) sizes.downloadButtonWidth = 17 sizes.descriptionTruncateSize = sizes.width - 6 - MineOSCore.iconWidth - sizes.downloadButtonWidth + sizes.searchFieldWidth = math.floor(sizes.width * 0.3) + obj.searchTextField = GUI.textField(math.floor(sizes.x + sizes.width / 2 - sizes.searchFieldWidth / 2), 1, sizes.searchFieldWidth, 1, 0xEEEEEE, 0x777777, 0xEEEEEE, 0x555555, nil, "Поиск", false, true) end local function drawTopBar() @@ -184,26 +187,33 @@ local function drawPageSwitchButtons(y) end local function clearMainZone() - buffer.square(sizes.x, sizes.yMain, sizes.width, sizes.mainHeight, 0xFFFFFF) + buffer.square(sizes.x, obj.main.y, sizes.width, obj.main.height, 0xFFFFFF) end local function drawMain(refreshData) clearMainZone() local x, y = sizes.x + 2, fromY - buffer.setDrawLimit(sizes.x, sizes.yMain, sizes.width, sizes.mainHeight) + buffer.setDrawLimit(sizes.x, obj.main.y, sizes.width, obj.main.height) + + obj.searchTextField.y, obj.searchTextField.invisible = y, false + obj.searchTextField:draw() + y = y + 2 local matchCount = 1 for i = 1, #newApplications do if newApplications[i].type == typeFilters[currentTopBarElement] then - if matchCount >= from and matchCount <= from + limit - 1 then - if refreshData and not currentApps[i] then - status("Загрузка информации о приложении \"" .. newApplications[i].name .. "\"") - getApplication(i) + -- if obj.searchTextField.text then GUI.error(tostring(unicode.lower(obj.searchTextField.text))) end + if not obj.searchTextField.text or (string.find(unicode.lower(fs.name(newApplications[i].name)), unicode.lower(obj.searchTextField.text))) then + if matchCount >= from and matchCount <= from + limit - 1 then + if refreshData and not currentApps[i] then + status("Загрузка информации о приложении \"" .. newApplications[i].name .. "\"") + getApplication(i) + end + x, y = drawApplication(x, y, i) end - x, y = drawApplication(x, y, i) + matchCount = matchCount + 1 end - matchCount = matchCount + 1 end end @@ -226,8 +236,7 @@ local function getChanges() for j = 1, #newApplications do if oldApplications[i].name == newApplications[j].name then if oldApplications[i].version < newApplications[j].version then - table.insert(changes, newApplications[j]) - changes[#changes].newApplicationsIndex = j + table.insert(changes, j) end end end @@ -238,18 +247,18 @@ local function updates() clearMainZone() if #changes > 0 then - buffer.setDrawLimit(sizes.x, sizes.yMain, sizes.width, sizes.mainHeight) + buffer.setDrawLimit(sizes.x, obj.main.y, sizes.width, obj.main.height) local x, y = sizes.x + 2, fromY obj.updateAllButton = GUI.button(math.floor(sizes.x + sizes.width / 2 - sizes.downloadButtonWidth / 2), y, 20, 1, colors.downloadButton, colors.downloadButtonText, 0x555555, 0xFFFFFF, "Обновить все") y = y + 2 for i = from, (from + limit) do if not changes[i] then break end - if not currentApps[changes[i].newApplicationsIndex] then - status("Загрузка информации о приложении \"" .. changes[i].name .. "\"") - getApplication(changes[i].newApplicationsIndex) + if not currentApps[changes[i]] then + status("Загрузка информации о приложении \"" .. fs.name(newApplications[changes[i]].name) .. "\"") + getApplication(changes[i]) end - x, y = drawApplication(x, y, changes[i].newApplicationsIndex, true) + x, y = drawApplication(x, y, changes[i], true) end if #changes > limit then @@ -258,12 +267,12 @@ local function updates() buffer.resetDrawLimit() else local text = "У вас самое новое ПО" - buffer.text(math.floor(sizes.x + sizes.width / 2 - unicode.len(text) / 2), math.floor(sizes.yMain + sizes.mainHeight / 2 - 1), colors.description, text) + buffer.text(math.floor(sizes.x + sizes.width / 2 - unicode.len(text) / 2), math.floor(obj.main.y + obj.main.height / 2 - 1), colors.description, text) end end local function flush() - fromY = sizes.yMain + 1 + fromY = obj.main.y + 1 from = 1 fs.makeDirectory(appStorePath) currentApps = {} @@ -289,7 +298,7 @@ end local function updateImageWindow() clearMainZone() - local x, y = math.floor(sizes.x + sizes.width / 2 - updateImage.width / 2), math.floor(sizes.yMain + sizes.mainHeight / 2 - updateImage.height / 2 - 2) + local x, y = math.floor(sizes.x + sizes.width / 2 - updateImage.width / 2), math.floor(obj.main.y + obj.main.height / 2 - updateImage.height / 2 - 2) buffer.image(x, y, updateImage) return y + updateImage.height end @@ -306,13 +315,13 @@ local function updateAll() local xBar = math.floor(sizes.x + sizes.width / 2 - barWidth / 2) y = y + 2 for i = 1, #changes do - local text = "Обновление " .. fs.name(changes[i].name) + local text = "Обновление " .. fs.name(newApplications[changes[i]].name) local xText = math.floor(sizes.x + sizes.width / 2 - unicode.len(text) / 2) buffer.square(sizes.x, y + 1, sizes.width, 1, 0xFFFFFF) buffer.text(xText, y + 1, colors.description, text) GUI.progressBar(xBar, y, barWidth, 1, 0xAAAAAA, 0x55FF55, i, #changes, true) buffer.draw() - ecs.getOSApplication(newApplications[changes[i].newApplicationsIndex], true) + ecs.getOSApplication(newApplications[changes[i]], true) end changes = {} oldApplications = newApplications @@ -321,8 +330,8 @@ end ------------------------------------------------------------------------------------------------------------------ --- buffer.start() --- buffer.clear(0xFF8888) +buffer.start() +buffer.clear(0xFF8888) local args = {...} if args[1] == "updateCheck" then @@ -343,51 +352,61 @@ drawAll(true, false) while true do local e = {event.pull()} if e[1] == "touch" then - if currentTopBarElement < 5 then - for appIndex, app in pairs(currentApps) do - if app.buttonObject:isClicked(e[3], e[4]) then - app.buttonObject:press(0.3) - if app.buttonObject.text == "Обновить" or app.buttonObject.text == "Загрузить" then - app.buttonObject.text = "Загрузка" - app.buttonObject.disabled = true - app.buttonObject.colors.disabled.button, app.buttonObject.colors.disabled.text = colors.downloading, colors.downloadingText - app.buttonObject:draw() - buffer.draw() - ecs.getOSApplication(newApplications[appIndex], true) - app.buttonObject.text = "Установлено" - app.buttonObject.colors.disabled.button, app.buttonObject.colors.disabled.text = colors.downloaded, colors.downloadedText - app.buttonObject:draw() - buffer.draw() - end - break - end - end - else - if obj.updateAllButton and obj.updateAllButton:isClicked(e[3], e[4]) then - obj.updateAllButton:press() - updateAll() - flush() - drawAll() - end - end - if obj.nextPageButton then - if obj.nextPageButton:isClicked(e[3], e[4]) then - obj.nextPageButton:press() - fromY = sizes.yMain + 1 - from = from + limit - currentApps = {} + if obj.main:isClicked(e[3], e[4]) then + if obj.searchTextField:isClicked(e[3], e[4]) then + obj.searchTextField:input() + flush() drawAll(true, false) - elseif obj.prevPageButton:isClicked(e[3], e[4]) then - if from > limit then - fromY = sizes.yMain + 1 - from = from - limit + end + + if currentTopBarElement < 5 then + for appIndex, app in pairs(currentApps) do + if app.buttonObject:isClicked(e[3], e[4]) then + app.buttonObject:press(0.3) + if app.buttonObject.text == "Обновить" or app.buttonObject.text == "Загрузить" then + app.buttonObject.text = "Загрузка" + app.buttonObject.disabled = true + app.buttonObject.colors.disabled.button, app.buttonObject.colors.disabled.text = colors.downloading, colors.downloadingText + app.buttonObject:draw() + buffer.draw() + ecs.getOSApplication(newApplications[appIndex], true) + app.buttonObject.text = "Установлено" + app.buttonObject.colors.disabled.button, app.buttonObject.colors.disabled.text = colors.downloaded, colors.downloadedText + app.buttonObject:draw() + buffer.draw() + end + break + end + end + else + if obj.updateAllButton and obj.updateAllButton:isClicked(e[3], e[4]) then + obj.updateAllButton:press() + updateAll() + flush() + drawAll() + end + end + + if obj.nextPageButton then + if obj.nextPageButton:isClicked(e[3], e[4]) then + obj.nextPageButton:press() + fromY = obj.main.y + 1 + from = from + limit currentApps = {} drawAll(true, false) + elseif obj.prevPageButton:isClicked(e[3], e[4]) then + if from > limit then + fromY = obj.main.y + 1 + from = from - limit + currentApps = {} + drawAll(true, false) + end end end end + if obj.windowActionButtons.close:isClicked(e[3], e[4]) then obj.windowActionButtons.close:press() return @@ -403,7 +422,7 @@ while true do end elseif e[1] == "scroll" then if e[5] == 1 then - if (fromY < sizes.yMain) then + if (fromY < obj.main.y) then fromY = fromY + 2 drawAll(false, false) end diff --git a/lib/GUI.lua b/lib/GUI.lua index 22be2829..ea7fe139 100644 --- a/lib/GUI.lua +++ b/lib/GUI.lua @@ -2,7 +2,8 @@ local libraries = { buffer = "doubleBuffering", ecs = "ECSAPI", - unicode = "unicode" + unicode = "unicode", + event = "event", } for library in pairs(libraries) do if not _G[library] then _G[library] = require(libraries[library]) end end; libraries = nil @@ -24,11 +25,12 @@ GUI.buttonTypes = { GUI.colors = { disabled = 0x888888, + disabledText = 0xAAAAAA, } -- Универсальный метод для проверки клика на прямоугольный объект local function objectClicked(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 and not object.disabled then return true end + if x >= object.x and y >= object.y and x <= object.x + object.width - 1 and y <= object.y + object.height - 1 and not object.disabled and not object.invisible ~= false then return true end return false end @@ -91,7 +93,7 @@ local function createButtonObject(buttonType, x, y, width, height, buttonColor, }, disabled = { button = GUI.colors.disabled, - text = GUI.colors.disabled, + text = GUI.colors.disabledText, } } buttonObject.disabled = disabledState @@ -329,6 +331,8 @@ end -- } function GUI.input(x, y, width, foreground, startText, textFieldProperties) + if not (y >= buffer.drawLimit.y and y <= buffer.drawLimit.y2) then return startText end + local text = startText local textLength = unicode.len(text) local cursorBlinkState = false @@ -424,6 +428,51 @@ function GUI.input(x, y, width, foreground, startText, textFieldProperties) end end +local function drawTextField(object, isFocused) + if not object.invisible then + local background = object.disabled and object.colors.disabled.textField or (isFocused and object.colors.focused.textField or object.colors.default.textField) + local foreground = object.disabled and object.colors.disabled.text or (isFocused and object.colors.focused.text or object.colors.default.text) + local y = math.floor(object.y + object.height / 2) + local text = isFocused and (object.text or "") or (object.text or object.placeholderText or "") + + if background then buffer.square(object.x, object.y, object.width, object.height, background, foreground, " ") end + local resultText = GUI.input(object.x + 1, y, object.width - 2, foreground, text, {justDrawNotEvent = not isFocused}) + object.text = isFocused and resultText or object.text + end +end + +local function textFieldBeginInput(object) + drawTextField(object, true) + if object.text == "" then object.text = nil; drawTextField(object, false); buffer.draw() end +end + +function GUI.textField(x, y, width, height, textFieldColor, textColor, textFieldFocusedColor, textFocusedColor, text, placeholderText, disabledState, invisibleState) + local object = GUI.object(x, y, width, height) + object.invisible = invisibleState + object.disabled = disabledState + object.colors = { + default = { + textField = textFieldColor, + text = textColor + }, + focused = { + textField = textFieldFocusedColor, + text = textFocusedColor + }, + disabled = { + textField = GUI.colors.disabled, + text = GUI.colors.disabledText, + } + } + object.text = text + object.placeholderText = placeholderText + object.isClicked = objectClicked + object.draw = drawTextField + object.input = textFieldBeginInput + object:draw() + return object +end + -------------------------------------------------------------------------------------------------------------------------------- diff --git a/lib/doubleBuffering.lua b/lib/doubleBuffering.lua index ff0f141e..946629f6 100644 --- a/lib/doubleBuffering.lua +++ b/lib/doubleBuffering.lua @@ -51,12 +51,12 @@ end -- Установить ограниченную зону рисования. Все пиксели, не попадающие в эту зону, будут игнорироваться. function buffer.setDrawLimit(x, y, width, height) - buffer.drawLimit = { x1 = x, y1 = y, x2 = x + width - 1, y2 = y + height - 1 } + buffer.drawLimit = { x = x, y = y, x2 = x + width - 1, y2 = y + height - 1 } end -- Удалить ограничение зоны рисования, по умолчанию она будет от 1х1 до координат размера экрана. function buffer.resetDrawLimit() - buffer.drawLimit = {x1 = 1, y1 = 1, x2 = buffer.screen.width, y2 = buffer.screen.height} + buffer.drawLimit = {x = 1, y = 1, x2 = buffer.screen.width, y2 = buffer.screen.height} end -- Создать массив буфера с базовыми переменными и базовыми цветами. Т.е. черный фон, белый текст. @@ -96,7 +96,7 @@ end -- Установить пиксель в буфере function buffer.set(x, y, background, foreground, symbol) local index = convertCoordsToIndex(x, y) - if x >= buffer.drawLimit.x1 and y >= buffer.drawLimit.y1 and x <= buffer.drawLimit.x2 and y <= buffer.drawLimit.y2 then + if x >= buffer.drawLimit.x and y >= buffer.drawLimit.y and x <= buffer.drawLimit.x2 and y <= buffer.drawLimit.y2 then buffer.screen.new[index] = background buffer.screen.new[index + 1] = foreground buffer.screen.new[index + 2] = symbol @@ -113,7 +113,7 @@ function buffer.square(x, y, width, height, background, foreground, symbol, tran for j = y, (y + height - 1) do for i = x, (x + width - 1) do - if i >= buffer.drawLimit.x1 and j >= buffer.drawLimit.y1 and i <= buffer.drawLimit.x2 and j <= buffer.drawLimit.y2 then + if i >= buffer.drawLimit.x and j >= buffer.drawLimit.y and i <= buffer.drawLimit.x2 and j <= buffer.drawLimit.y2 then index = convertCoordsToIndex(i, j) indexPlus1 = index + 1 indexPlus2 = index + 2 @@ -156,7 +156,7 @@ function buffer.fill(x, y, background, foreground, symbol) end --Заливаем в память - if xStart >= buffer.drawLimit.x1 and yStart >= buffer.drawLimit.y1 and xStart <= buffer.drawLimit.x2 and yStart <= buffer.drawLimit.y2 then + if xStart >= buffer.drawLimit.x and yStart >= buffer.drawLimit.y and xStart <= buffer.drawLimit.x2 and yStart <= buffer.drawLimit.y2 then buffer.screen.new[index] = background buffer.screen.new[index + 1] = foreground buffer.screen.new[index + 2] = symbol @@ -242,7 +242,7 @@ function buffer.paste(x, y, copyArray) for j = y, (y + copyArray.height - 1) do for i = x, (x + copyArray.width - 1) do - if i >= buffer.drawLimit.x1 and j >= buffer.drawLimit.y1 and i <= buffer.drawLimit.x2 and j <= buffer.drawLimit.y2 then + if i >= buffer.drawLimit.x and j >= buffer.drawLimit.y and i <= buffer.drawLimit.x2 and j <= buffer.drawLimit.y2 then --Рассчитываем индекс массива основного изображения index = convertCoordsToIndex(i, j) --Копипаст формулы, аккуратнее! @@ -292,7 +292,7 @@ function buffer.text(x, y, color, text, transparency) if transparency then transparency = transparency * 2.55 end local sText = unicode.len(text) for i = 1, sText do - if (x + i - 1) >= buffer.drawLimit.x1 and y >= buffer.drawLimit.y1 and (x + i - 1) <= buffer.drawLimit.x2 and y <= buffer.drawLimit.y2 then + if (x + i - 1) >= buffer.drawLimit.x and y >= buffer.drawLimit.y and (x + i - 1) <= buffer.drawLimit.x2 and y <= buffer.drawLimit.y2 then index = convertCoordsToIndex(x + i - 1, y) buffer.screen.new[index + 1] = not transparency and color or colorlib.alphaBlend(buffer.screen.new[index], color, transparency) buffer.screen.new[index + 2] = unicode.sub(text, i, i) @@ -307,7 +307,7 @@ function buffer.image(x, y, picture) for j = y, (y + picture.height - 1) do for i = x, (x + picture.width - 1) do - if i >= buffer.drawLimit.x1 and j >= buffer.drawLimit.y1 and i <= buffer.drawLimit.x2 and j <= buffer.drawLimit.y2 then + if i >= buffer.drawLimit.x and j >= buffer.drawLimit.y and i <= buffer.drawLimit.x2 and j <= buffer.drawLimit.y2 then index = convertCoordsToIndex(i, j) indexPlus1 = index + 1 indexPlus2 = index + 2