From c56d06bfb20f61d5ae813270bee75e43c412ee93 Mon Sep 17 00:00:00 2001 From: Igor Timofeev Date: Mon, 16 Oct 2017 20:14:58 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D1=80=D0=BF=D1=80=D0=B5=D1=82=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B0=20Lua=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F=20Control?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Applications.cfg | 8 +- .../Control/Localization/English.lang | 14 +- .../Control/Localization/Russian.lang | 14 +- Applications/Control/Modules/1.lua | 222 ++++-- Applications/Control/Modules/2.lua | 183 ++--- Applications/Control/Modules/3.lua | 138 +++- Applications/Control/Modules/4.lua | 53 ++ Applications/MineCodeIDE/Main.lua | 4 +- lib/GUI.lua | 674 +++++++++++++----- lib/advancedLua.lua | 6 +- 10 files changed, 922 insertions(+), 394 deletions(-) create mode 100644 Applications/Control/Modules/4.lua diff --git a/Applications.cfg b/Applications.cfg index c42ee3ed..b9d61561 100644 --- a/Applications.cfg +++ b/Applications.cfg @@ -249,7 +249,7 @@ url="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/advancedLua.lua", type="Library", preloadFile=true, - version=1.24, + version=1.26, }, { path="/lib/web.lua", @@ -302,7 +302,7 @@ url="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/lib/GUI.lua", type="Library", preloadFile=true, - version=1.85, + version=1.86, }, { path="/lib/rayEngine.lua", @@ -464,7 +464,7 @@ type="Application", icon="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/Applications/MineCodeIDE/Icon.pic", forceDownload=true, - version=1.81, + version=1.82, resources={ { path="/Localization/Russian.lang", @@ -896,7 +896,7 @@ type="Application", icon="https://raw.githubusercontent.com/IgorTimofeev/OpenComputers/master/Applications/Control/Icon.pic", forceDownload=true, - version=5.03, + version=5.04, resources={ { path="/Localization/Russian.lang", diff --git a/Applications/Control/Localization/English.lang b/Applications/Control/Localization/English.lang index 4a768d3e..41fd3ad3 100755 --- a/Applications/Control/Localization/English.lang +++ b/Applications/Control/Localization/English.lang @@ -1,7 +1,8 @@ { - moduleDisk = "Disk utility", - moduleRAM = "RAM control", - moduleEvent = "Event processing", + moduleDisk = "Disks", + moduleRAM = "RAM", + moduleEvent = "Events", + moduleLua = "Lua interpreter", diskLabel = "Disk label", bootable = "Bootable", @@ -13,4 +14,11 @@ execute = "Execute", waitingEvents = "Waiting for events", processingEnabled = "Event processing enabled", + luaInfo = { + {color = 0x880000, text = _G._VERSION .. " Copyright (C) 1994-2017 Lua.org, PUC-Rio"}, + "Type a statement and hit Enter to evaluate it", + "Prefix an expression with \"=\" to show its value", + " " + }, + luaType = "Type statement here" } \ No newline at end of file diff --git a/Applications/Control/Localization/Russian.lang b/Applications/Control/Localization/Russian.lang index d42310ea..7ce06038 100755 --- a/Applications/Control/Localization/Russian.lang +++ b/Applications/Control/Localization/Russian.lang @@ -1,7 +1,8 @@ { - moduleDisk = "Дисковая утилита", - moduleRAM = "Управление памятью", - moduleEvent = "Анализ событий", + moduleDisk = "Диски", + moduleRAM = "Память", + moduleEvent = "События", + moduleLua = "Интерпретатор Lua", diskLabel = "Имя диска", bootable = "Загрузочный", @@ -13,4 +14,11 @@ execute = "Выполнить", waitingEvents = "Ожидание событий", processingEnabled = "Обработка событий", + luaInfo = { + {color = 0x880000, text = _G._VERSION .. " Copyright (C) 1994-2017 Lua.org, PUC-Rio"}, + "Введите выражение и нажмите Enter, чтобы выполнить его", + "Используйте префикс \"=\", чтобы вывести значение переменной", + " ", + }, + luaType = "Введите выражение здесь" } \ No newline at end of file diff --git a/Applications/Control/Modules/1.lua b/Applications/Control/Modules/1.lua index 257c3ac7..3057bb05 100644 --- a/Applications/Control/Modules/1.lua +++ b/Applications/Control/Modules/1.lua @@ -10,94 +10,168 @@ local buffer = require("doubleBuffering") local image = require("image") local MineOSPaths = require("MineOSPaths") local MineOSInterface = require("MineOSInterface") +local unicode = require("unicode") +local syntax = require("syntax") ---------------------------------------------------------------------------------------------------------------- local module = {} -module.name = localization.moduleDisk - -local HDDImage = image.load(MineOSPaths.icons .. "HDD.pic") -local floppyImage = image.load(MineOSPaths.icons .. "Floppy.pic") +module.name = localization.moduleLua ---------------------------------------------------------------------------------------------------------------- module.onTouch = function() window.contentContainer:deleteChildren() - local container = window.contentContainer:addChild(GUI.container(1, 1, window.contentContainer.width, window.contentContainer.height)) + + local textBox = window.contentContainer:addChild(GUI.textBox(1, 1, window.contentContainer.width, window.contentContainer.height - 3, nil, 0x444444, localization.luaInfo, 1, 2, 1)) + textBox.scrollBarEnabled = true + + local placeholder = localization.luaType + local input = window.contentContainer:addChild(GUI.input(1, window.contentContainer.height - 2, window.contentContainer.width, 3, 0x2D2D2D, 0xE1E1E1, 0x666666, 0x2D2D2D, 0xE1E1E1, "", placeholder, true)) - local y = 2 - for address in component.list("filesystem") do - local proxy = component.proxy(address) - local isBoot = computer.getBootAddress() == proxy.address - local isReadOnly = proxy.isReadOnly() - - local diskContainer = container:addChild(GUI.container(1, y, container.width, 4)) - - local button = diskContainer:addChild(GUI.adaptiveRoundedButton(1, 3, 2, 0, 0x2D2D2D, 0xE1E1E1, 0x0, 0xE1E1E1, localization.options)) - button.onTouch = function() - local container = MineOSInterface.addUniversalContainer(mainContainer, localization.options) - local inputField = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, proxy.getLabel() or "", localization.diskLabel)) - inputField.onInputFinished = function() - if inputField.text and inputField.text:len() > 0 then - proxy.setLabel(inputField.text) - - container:delete() - module.onTouch() - end - end - - local button = container.layout:addChild(GUI.roundedButton(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, localization.format)) - button.onTouch = function() - local list = proxy.list("/") - for i = 1, #list do - proxy.remove(list[i]) - end - - container:delete() - module.onTouch() - end - button.disabled = isReadOnly - - local switch = container.layout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0x1E1E1E, 0xEEEEEE, 0xBBBBBB, localization.bootable .. ":", isBoot)).switch - switch.onStateChanged = function() - if switch.state then - computer.setBootAddress(proxy.address) - - container:delete() - module.onTouch() - end - end - - mainContainer:draw() - buffer.draw() + input.textDrawMethod = function(x, y, color, text) + if text == placeholder then + buffer.text(x, y, color, text) + else + syntax.highlightString(x, y, text, 2) end - button.localPosition.x = diskContainer.width - button.width - 1 - - local width = diskContainer.width - button.width - 17 - local x = 13 - local spaceTotal = proxy.spaceTotal() - local spaceUsed = proxy.spaceUsed() - - diskContainer:addChild(GUI.image(3, 1, isReadOnly and floppyImage or HDDImage)) - diskContainer:addChild(GUI.label(x, 1, width, 1, 0x2D2D2D, (proxy.getLabel() or "Unknown") .. " (" .. (isBoot and (localization.bootable .. ", ") or "") .. proxy.address .. ")")) - diskContainer:addChild(GUI.progressBar(x, 3, width, 0x66DB80, 0xD2D2D2, 0xD2D2D2, spaceUsed / spaceTotal * 100, true)) - diskContainer:addChild(GUI.label(x, 4, width, 1, 0xBBBBBB, localization.free .. " " .. math.roundToDecimalPlaces((spaceTotal - spaceUsed) / 1024 / 1024, 2) .. " MB " .. localization.of .. " " .. math.roundToDecimalPlaces(spaceTotal / 1024 / 1024, 2) .. " MB")):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) - - y = y + diskContainer.height + 1 end - container.eventHandler = function(mainContainer, object, eventData) - if eventData[1] == "scroll" then - if eventData[5] < 0 or container.children[1].localPosition.y < 2 then - for i = 1, #container.children do - container.children[i].localPosition.y = container.children[i].localPosition.y + eventData[5] - end - - mainContainer:draw() - buffer.draw() + local function add(data, color) + for line in data:gmatch("[^\n]+") do + local wrappedLine = string.wrap(line, textBox.textWidth) + for i = 1, #wrappedLine do + table.insert(textBox.lines, color and {color = color, text = wrappedLine[i]} or wrappedLine[i]) end - elseif eventData[1] == "component_added" or eventData[1] == "component_removed" and eventData[3] == "filesystem" then - module.onTouch() + end + + textBox:scrollToEnd() + -- local abc = " "; for i = 1, 30 do abc = abc .. "p " end; print(abc) + end + + input.autoComplete.colors.default.background = 0x4B4B4B + input.autoComplete.colors.default.text = 0xC3C3C3 + input.autoComplete.colors.default.textMatch = 0xFFFFFF + input.autoComplete.colors.selected.background = 0x777777 + input.autoComplete.colors.selected.text = 0xD2D2D2 + input.autoComplete.colors.selected.textMatch = 0xFFFFFF + input.autoComplete.scrollBar.colors.background = 0x666666 + input.autoComplete.scrollBar.colors.foreground = 0xAAAAAA + + input.autoCompleteVerticalAlignment = GUI.alignment.vertical.top + input.autoCompleteEnabled = true + input.autoCompleteMatchMethod = function() + local inputTextLength = unicode.len(input.text) + local left, right = 1, inputTextLength + for i = input.cursorPosition - 1, 1, -1 do + if not unicode.sub(input.text, i, i):match("[%w%.]+") then + left = i + 1 + break + end + end + for i = input.cursorPosition, inputTextLength do + if not unicode.sub(input.text, i, i):match("[%w%.]+") then + right = i - 1 + break + end + end + local cykaText = unicode.sub(input.text, left, right) + + local array = {} + local t = _G + if cykaText:match("^[%w%.]+$") then + local words = {} + for word in cykaText:gmatch("[^%.]+") do + table.insert(words, word) + end + local dotInEnd = unicode.sub(cykaText, -1, -1) == "." + local wordCount = #words - (dotInEnd and 0 or 1) + + for i = 1, wordCount do + if t[words[i]] and type(t[words[i]]) == "table" then + t = t[words[i]] + else + input.autoComplete:clear() + return + end + end + + input.autoComplete.result = unicode.sub(input.text, 1, left - 1) + if wordCount > 0 then + input.autoComplete.result = input.autoComplete.result .. table.concat(words, ".", 1, wordCount) .. "." + end + + if dotInEnd then + for key, value in pairs(t) do + table.insert(array, tostring(key)) + end + input.autoComplete:match(array) + else + for key, value in pairs(t) do + table.insert(array, tostring(key)) + end + input.autoComplete:match(array, words[#words]) + end + elseif input.text == "" then + input.autoComplete.result = "" + for key, value in pairs(t) do + table.insert(array, tostring(key)) + end + input.autoComplete:match(array) + end + end + + input.autoComplete.onItemSelected = function() + input.text = input.autoComplete.result .. input.autoComplete.items[input.autoComplete.selectedItem] + input:setCursorPosition(unicode.len(input.text) + 1) + + if input.autoCompleteEnabled then + input.autoCompleteMatchMethod() + end + + mainContainer:draw() + buffer.draw() + end + + input.onInputFinished = function() + if input.text:len() > 0 then + local oldPrint = print + + print = function(...) + local args = {...} + for i = 1, #args do + if type(args[i]) == "table" then + args[i] = table.toString(args[i], true, 2, false, 2) + else + args[i] = tostring(args[i]) + end + end + add(table.concat(args, " ")) + end + + add("> " .. input.text, 0xAAAAAA) + + if unicode.sub(input.text, 1, 1) == "=" then + input.text = "return " .. unicode.sub(input.text, 2, -1) + end + + local result, reason = load(input.text) + if result then + local data = {xpcall(result, debug.traceback())} + if data[1] then + print(table.unpack(data, 2)) + else + add(tostring(data[2]), 0x880000) + end + else + add(tostring(reason), 0x880000) + end + + print = oldPrint + + input.text = "" + input.textCutFrom = 1 + input:setCursorPosition(1) end end diff --git a/Applications/Control/Modules/2.lua b/Applications/Control/Modules/2.lua index 441cc01a..257c3ac7 100644 --- a/Applications/Control/Modules/2.lua +++ b/Applications/Control/Modules/2.lua @@ -10,135 +10,96 @@ local buffer = require("doubleBuffering") local image = require("image") local MineOSPaths = require("MineOSPaths") local MineOSInterface = require("MineOSInterface") -local unicode = require("unicode") ---------------------------------------------------------------------------------------------------------------- local module = {} -module.name = localization.moduleRAM +module.name = localization.moduleDisk + +local HDDImage = image.load(MineOSPaths.icons .. "HDD.pic") +local floppyImage = image.load(MineOSPaths.icons .. "Floppy.pic") ---------------------------------------------------------------------------------------------------------------- -module.onTouch = function() +module.onTouch = function() window.contentContainer:deleteChildren() + local container = window.contentContainer:addChild(GUI.container(1, 1, window.contentContainer.width, window.contentContainer.height)) - local cykaPanel = window.contentContainer:addChild(GUI.panel(1, 1, 1, 1, 0xE1E1E1)) + local y = 2 + for address in component.list("filesystem") do + local proxy = component.proxy(address) + local isBoot = computer.getBootAddress() == proxy.address + local isReadOnly = proxy.isReadOnly() - local mainLayout = window.contentContainer:addChild(GUI.layout(1, 1, window.contentContainer.width, window.contentContainer.height, 2, 1)) - mainLayout:setColumnWidth(1, GUI.sizePolicies.percentage, 0.3) - mainLayout:setColumnWidth(2, GUI.sizePolicies.percentage, 0.7) - mainLayout:setCellFitting(1, 1, true, true) - mainLayout:setCellFitting(2, 1, true, true) - - local tree = mainLayout:setCellPosition(1, 1, mainLayout:addChild(GUI.tree(1, 1, 1, 1, 0xE1E1E1, 0x3C3C3C, 0x3C3C3C, 0xAAAAAA, 0x3C3C3C, 0xFFFFFF, 0xBBBBBB, 0xAAAAAA, 0xC3C3C3, 0x444444, GUI.filesystemModes.both, GUI.filesystemModes.file))) - - local itemsLayout = mainLayout:setCellPosition(2, 1, mainLayout:addChild(GUI.layout(1, 1, 1, 1, 1, 2))) - itemsLayout:setRowHeight(1, GUI.sizePolicies.percentage, 0.6) - itemsLayout:setRowHeight(2, GUI.sizePolicies.percentage, 0.4) - itemsLayout:setCellFitting(1, 1, true, false, 4, 0) - itemsLayout:setCellFitting(1, 2, true, true) - - local infoLabel = itemsLayout:setCellPosition(1, 1, itemsLayout:addChild(GUI.label(1, 1, 1, 1, 0x3C3C3C, "nil")):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)) - local argumentsInputField = itemsLayout:setCellPosition(1, 1, itemsLayout:addChild(GUI.input(1, 1, 1, 3, 0xFFFFFF, 0x666666, 0x888888, 0xFFFFFF, 0x262626, nil, localization.arguments))) - local executeButton = itemsLayout:setCellPosition(1, 1, itemsLayout:addChild(GUI.button(1, 1, 1, 3, 0x3C3C3C, 0xFFFFFF, 0x0, 0xFFFFFF, localization.execute))) - local outputTextBox = itemsLayout:setCellPosition(1, 2, itemsLayout:addChild(GUI.textBox(1, 1, 1, 1, 0xFFFFFF, 0x888888, {"nil"}, 1, 1, 0))) - - local function updateList(tree, t, definitionName, offset) - local list = {} - for key in pairs(t) do - table.insert(list, key) - end - - local i, expandables = 1, {} - while i <= #list do - if type(t[list[i]]) == "table" then - table.insert(expandables, list[i]) - table.remove(list, i) - else - i = i + 1 - end - end - - table.sort(expandables, function(a, b) return unicode.lower(tostring(a)) < unicode.lower(tostring(b)) end) - table.sort(list, function(a, b) return unicode.lower(tostring(a)) < unicode.lower(tostring(b)) end) - - for i = 1, #expandables do - local definition = definitionName .. expandables[i] - - tree:addItem(tostring(expandables[i]), definition, offset, true) - if tree.expandedItems[definition] then - updateList(tree, t[expandables[i]], definition, offset + 2) - end - end - - for i = 1, #list do - tree:addItem(tostring(list[i]), {key = list[i], value = t[list[i]]}, offset, false) - end - end - - local function out(text) - local wrappedLines = string.wrap(text, outputTextBox.width - 2) - for i = 1, #wrappedLines do - table.insert(outputTextBox.lines, wrappedLines[i]) - end - end - - tree.onItemExpanded = function() - tree.items = {} - updateList(tree, _G, "_G", 1) - end - - tree.onItemSelected = function() - local valueType = type(tree.selectedItem.value) - local valueIsFunction = valueType == "function" + local diskContainer = container:addChild(GUI.container(1, y, container.width, 4)) - executeButton.disabled = not valueIsFunction - argumentsInputField.disabled = executeButton.disabled + local button = diskContainer:addChild(GUI.adaptiveRoundedButton(1, 3, 2, 0, 0x2D2D2D, 0xE1E1E1, 0x0, 0xE1E1E1, localization.options)) + button.onTouch = function() + local container = MineOSInterface.addUniversalContainer(mainContainer, localization.options) + local inputField = container.layout:addChild(GUI.input(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, 0x262626, proxy.getLabel() or "", localization.diskLabel)) + inputField.onInputFinished = function() + if inputField.text and inputField.text:len() > 0 then + proxy.setLabel(inputField.text) - infoLabel.text = tostring(tree.selectedItem.key) .. " (" .. valueType .. ")" - outputTextBox.lines = {} - - if valueIsFunction then - out("nil") - else - out(tostring(tree.selectedItem.value)) - end - end - - executeButton.onTouch = function() - outputTextBox.lines = {} - - local data = "local method = ({...})[1]; return method(" .. (argumentsInputField.text or "") .. ")" - local success, reason = load(data) - if success then - local success, reason = pcall(success, tree.selectedItem.value) - if success then - if type(reason) == "table" then - local serialized = table.toString(reason, true, 2, false, 3) - for line in serialized:gmatch("[^\n]+") do - out(line) - end - else - out(tostring(reason)) + container:delete() + module.onTouch() end - else - out("Failed to pcall loaded string \"" .. data .. "\": " .. reason) end - else - out("Failed to load string \"" .. data .. "\": " .. reason) - end + + local button = container.layout:addChild(GUI.roundedButton(1, 1, 36, 3, 0xEEEEEE, 0x666666, 0x666666, 0xEEEEEE, localization.format)) + button.onTouch = function() + local list = proxy.list("/") + for i = 1, #list do + proxy.remove(list[i]) + end - mainContainer:draw() - buffer.draw() + container:delete() + module.onTouch() + end + button.disabled = isReadOnly + + local switch = container.layout:addChild(GUI.switchAndLabel(1, 1, 36, 8, 0x66DB80, 0x1E1E1E, 0xEEEEEE, 0xBBBBBB, localization.bootable .. ":", isBoot)).switch + switch.onStateChanged = function() + if switch.state then + computer.setBootAddress(proxy.address) + + container:delete() + module.onTouch() + end + end + + mainContainer:draw() + buffer.draw() + end + button.localPosition.x = diskContainer.width - button.width - 1 + + local width = diskContainer.width - button.width - 17 + local x = 13 + local spaceTotal = proxy.spaceTotal() + local spaceUsed = proxy.spaceUsed() + + diskContainer:addChild(GUI.image(3, 1, isReadOnly and floppyImage or HDDImage)) + diskContainer:addChild(GUI.label(x, 1, width, 1, 0x2D2D2D, (proxy.getLabel() or "Unknown") .. " (" .. (isBoot and (localization.bootable .. ", ") or "") .. proxy.address .. ")")) + diskContainer:addChild(GUI.progressBar(x, 3, width, 0x66DB80, 0xD2D2D2, 0xD2D2D2, spaceUsed / spaceTotal * 100, true)) + diskContainer:addChild(GUI.label(x, 4, width, 1, 0xBBBBBB, localization.free .. " " .. math.roundToDecimalPlaces((spaceTotal - spaceUsed) / 1024 / 1024, 2) .. " MB " .. localization.of .. " " .. math.roundToDecimalPlaces(spaceTotal / 1024 / 1024, 2) .. " MB")):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top) + + y = y + diskContainer.height + 1 end + container.eventHandler = function(mainContainer, object, eventData) + if eventData[1] == "scroll" then + if eventData[5] < 0 or container.children[1].localPosition.y < 2 then + for i = 1, #container.children do + container.children[i].localPosition.y = container.children[i].localPosition.y + eventData[5] + end - executeButton.disabled = true - argumentsInputField.disabled = true - executeButton.colors.disabled.background = 0x777777 - executeButton.colors.disabled.text = 0xD2D2D2 - - tree.onItemExpanded() + mainContainer:draw() + buffer.draw() + end + elseif eventData[1] == "component_added" or eventData[1] == "component_removed" and eventData[3] == "filesystem" then + module.onTouch() + end + end mainContainer:draw() buffer.draw() diff --git a/Applications/Control/Modules/3.lua b/Applications/Control/Modules/3.lua index 8d01ccc8..441cc01a 100644 --- a/Applications/Control/Modules/3.lua +++ b/Applications/Control/Modules/3.lua @@ -10,40 +10,136 @@ local buffer = require("doubleBuffering") local image = require("image") local MineOSPaths = require("MineOSPaths") local MineOSInterface = require("MineOSInterface") +local unicode = require("unicode") ---------------------------------------------------------------------------------------------------------------- local module = {} -module.name = localization.moduleEvent +module.name = localization.moduleRAM ---------------------------------------------------------------------------------------------------------------- -module.onTouch = function() +module.onTouch = function() window.contentContainer:deleteChildren() - - local container = window.contentContainer:addChild(GUI.container(1, 1, window.contentContainer.width, window.contentContainer.height)) - - local layout = container:addChild(GUI.layout(1, 1, container.width, window.contentContainer.height, 1, 1)) - layout:setCellAlignment(1, 1, GUI.alignment.horizontal.center, GUI.alignment.vertical.top) - layout:setCellMargin(1, 1, 0, 1) - - local textBox = layout:addChild(GUI.textBox(1, 1, container.width - 4, container.height - 4, nil, 0x888888, {localization.waitingEvents .. "..."}, 1, 0, 0)) - local switch = layout:addChild(GUI.switchAndLabel(1, 1, 27, 6, 0x66DB80, 0x1E1E1E, 0xFFFFFF, 0x2D2D2D, localization.processingEnabled .. ": ", true)).switch - container.eventHandler = function(mainContainer, object, eventData) - if switch.state and eventData[1] then - local lines = table.concat(eventData, " ") - lines = string.wrap(lines, textBox.width) - for i = 1, #lines do - table.insert(textBox.lines, lines[i]) - end - textBox:scrollDown(#lines) + local cykaPanel = window.contentContainer:addChild(GUI.panel(1, 1, 1, 1, 0xE1E1E1)) - mainContainer:draw() - buffer.draw() + local mainLayout = window.contentContainer:addChild(GUI.layout(1, 1, window.contentContainer.width, window.contentContainer.height, 2, 1)) + mainLayout:setColumnWidth(1, GUI.sizePolicies.percentage, 0.3) + mainLayout:setColumnWidth(2, GUI.sizePolicies.percentage, 0.7) + mainLayout:setCellFitting(1, 1, true, true) + mainLayout:setCellFitting(2, 1, true, true) + + local tree = mainLayout:setCellPosition(1, 1, mainLayout:addChild(GUI.tree(1, 1, 1, 1, 0xE1E1E1, 0x3C3C3C, 0x3C3C3C, 0xAAAAAA, 0x3C3C3C, 0xFFFFFF, 0xBBBBBB, 0xAAAAAA, 0xC3C3C3, 0x444444, GUI.filesystemModes.both, GUI.filesystemModes.file))) + + local itemsLayout = mainLayout:setCellPosition(2, 1, mainLayout:addChild(GUI.layout(1, 1, 1, 1, 1, 2))) + itemsLayout:setRowHeight(1, GUI.sizePolicies.percentage, 0.6) + itemsLayout:setRowHeight(2, GUI.sizePolicies.percentage, 0.4) + itemsLayout:setCellFitting(1, 1, true, false, 4, 0) + itemsLayout:setCellFitting(1, 2, true, true) + + local infoLabel = itemsLayout:setCellPosition(1, 1, itemsLayout:addChild(GUI.label(1, 1, 1, 1, 0x3C3C3C, "nil")):setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.top)) + local argumentsInputField = itemsLayout:setCellPosition(1, 1, itemsLayout:addChild(GUI.input(1, 1, 1, 3, 0xFFFFFF, 0x666666, 0x888888, 0xFFFFFF, 0x262626, nil, localization.arguments))) + local executeButton = itemsLayout:setCellPosition(1, 1, itemsLayout:addChild(GUI.button(1, 1, 1, 3, 0x3C3C3C, 0xFFFFFF, 0x0, 0xFFFFFF, localization.execute))) + local outputTextBox = itemsLayout:setCellPosition(1, 2, itemsLayout:addChild(GUI.textBox(1, 1, 1, 1, 0xFFFFFF, 0x888888, {"nil"}, 1, 1, 0))) + + local function updateList(tree, t, definitionName, offset) + local list = {} + for key in pairs(t) do + table.insert(list, key) + end + + local i, expandables = 1, {} + while i <= #list do + if type(t[list[i]]) == "table" then + table.insert(expandables, list[i]) + table.remove(list, i) + else + i = i + 1 + end + end + + table.sort(expandables, function(a, b) return unicode.lower(tostring(a)) < unicode.lower(tostring(b)) end) + table.sort(list, function(a, b) return unicode.lower(tostring(a)) < unicode.lower(tostring(b)) end) + + for i = 1, #expandables do + local definition = definitionName .. expandables[i] + + tree:addItem(tostring(expandables[i]), definition, offset, true) + if tree.expandedItems[definition] then + updateList(tree, t[expandables[i]], definition, offset + 2) + end + end + + for i = 1, #list do + tree:addItem(tostring(list[i]), {key = list[i], value = t[list[i]]}, offset, false) end end + local function out(text) + local wrappedLines = string.wrap(text, outputTextBox.width - 2) + for i = 1, #wrappedLines do + table.insert(outputTextBox.lines, wrappedLines[i]) + end + end + + tree.onItemExpanded = function() + tree.items = {} + updateList(tree, _G, "_G", 1) + end + + tree.onItemSelected = function() + local valueType = type(tree.selectedItem.value) + local valueIsFunction = valueType == "function" + + executeButton.disabled = not valueIsFunction + argumentsInputField.disabled = executeButton.disabled + + infoLabel.text = tostring(tree.selectedItem.key) .. " (" .. valueType .. ")" + outputTextBox.lines = {} + + if valueIsFunction then + out("nil") + else + out(tostring(tree.selectedItem.value)) + end + end + + executeButton.onTouch = function() + outputTextBox.lines = {} + + local data = "local method = ({...})[1]; return method(" .. (argumentsInputField.text or "") .. ")" + local success, reason = load(data) + if success then + local success, reason = pcall(success, tree.selectedItem.value) + if success then + if type(reason) == "table" then + local serialized = table.toString(reason, true, 2, false, 3) + for line in serialized:gmatch("[^\n]+") do + out(line) + end + else + out(tostring(reason)) + end + else + out("Failed to pcall loaded string \"" .. data .. "\": " .. reason) + end + else + out("Failed to load string \"" .. data .. "\": " .. reason) + end + + mainContainer:draw() + buffer.draw() + end + + + executeButton.disabled = true + argumentsInputField.disabled = true + executeButton.colors.disabled.background = 0x777777 + executeButton.colors.disabled.text = 0xD2D2D2 + + tree.onItemExpanded() + mainContainer:draw() buffer.draw() end diff --git a/Applications/Control/Modules/4.lua b/Applications/Control/Modules/4.lua new file mode 100644 index 00000000..1a291e76 --- /dev/null +++ b/Applications/Control/Modules/4.lua @@ -0,0 +1,53 @@ + +local args = {...} +local mainContainer, window, localization = args[1], args[2], args[3] + +require("advancedLua") +local component = require("component") +local computer = require("computer") +local GUI = require("GUI") +local buffer = require("doubleBuffering") +local image = require("image") +local MineOSPaths = require("MineOSPaths") +local MineOSInterface = require("MineOSInterface") + +---------------------------------------------------------------------------------------------------------------- + +local module = {} +module.name = localization.moduleEvent + +---------------------------------------------------------------------------------------------------------------- + +module.onTouch = function() + window.contentContainer:deleteChildren() + + local container = window.contentContainer:addChild(GUI.container(1, 1, window.contentContainer.width, window.contentContainer.height)) + + local layout = container:addChild(GUI.layout(1, 1, container.width, window.contentContainer.height, 1, 1)) + layout:setCellAlignment(1, 1, GUI.alignment.horizontal.center, GUI.alignment.vertical.top) + layout:setCellMargin(1, 1, 0, 1) + + local textBox = layout:addChild(GUI.textBox(1, 1, container.width - 4, container.height - 4, nil, 0x888888, {localization.waitingEvents .. "..."}, 1, 0, 0)) + local switch = layout:addChild(GUI.switchAndLabel(1, 1, 27, 6, 0x66DB80, 0x1E1E1E, 0xFFFFFF, 0x2D2D2D, localization.processingEnabled .. ": ", true)).switch + + container.eventHandler = function(mainContainer, object, eventData) + if switch.state and eventData[1] then + local lines = table.concat(eventData, " ") + lines = string.wrap(lines, textBox.width) + for i = 1, #lines do + table.insert(textBox.lines, lines[i]) + end + textBox:scrollToEnd() + + mainContainer:draw() + buffer.draw() + end + end + + mainContainer:draw() + buffer.draw() +end + +---------------------------------------------------------------------------------------------------------------- + +return module \ No newline at end of file diff --git a/Applications/MineCodeIDE/Main.lua b/Applications/MineCodeIDE/Main.lua index cdb164e4..ef6aa140 100755 --- a/Applications/MineCodeIDE/Main.lua +++ b/Applications/MineCodeIDE/Main.lua @@ -274,7 +274,7 @@ local function calculateSizes() mainContainer.codeView.height = mainContainer.codeView.height - 3 end - mainContainer.leftTreeViewResizer.localPosition.x = mainContainer.leftTreeView.width - 1 + mainContainer.leftTreeViewResizer.localPosition.x = mainContainer.leftTreeView.width - 2 mainContainer.leftTreeViewResizer.localPosition.y = math.floor(mainContainer.leftTreeView.localPosition.y + mainContainer.leftTreeView.height / 2 - mainContainer.leftTreeViewResizer.height / 2) mainContainer.settingsContainer.width, mainContainer.settingsContainer.height = mainContainer.width, mainContainer.height @@ -1587,7 +1587,7 @@ local function createMainContainer() end mainContainer.bottomToolBar.hidden = true - mainContainer.leftTreeView = mainContainer:addChild(GUI.filesystemTree(1, 1, config.leftTreeViewWidth, 1, 0xCCCCCC, 0x3C3C3C, 0x3C3C3C, 0x999999, 0x3C3C3C, 0xE1E1E1, 0xBBBBBB, 0xAAAAAA, 0xC3C3C3, 0x444444, GUI.filesystemModes.both, GUI.filesystemModes.file)) + mainContainer.leftTreeView = mainContainer:addChild(GUI.filesystemTree(1, 1, config.leftTreeViewWidth, 1, 0xCCCCCC, 0x3C3C3C, 0x3C3C3C, 0x999999, 0x3C3C3C, 0xE1E1E1, 0xBBBBBB, 0xAAAAAA, 0xBBBBBB, 0x444444, GUI.filesystemModes.both, GUI.filesystemModes.file)) mainContainer.leftTreeView.onItemSelected = function(path) loadFile(path) diff --git a/lib/GUI.lua b/lib/GUI.lua index 3d7b9b41..e217e29c 100755 --- a/lib/GUI.lua +++ b/lib/GUI.lua @@ -1233,8 +1233,8 @@ end ----------------------------------------- Window object ----------------------------------------- local function windowDraw(window) - GUI.drawContainerContent(window) GUI.windowShadow(window.x, window.y, window.width, window.height, nil, true) + GUI.drawContainerContent(window) return window end @@ -1720,138 +1720,6 @@ function GUI.switchAndLabel(x, y, width, switchWidth, activeColor, passiveColor, return switchAndLabel end ------------------------------------------ Text Box object ----------------------------------------- - -local function textBoxCalculate(object) - local doubleVerticalOffset = object.offset.vertical * 2 - object.textWidth = object.width - object.offset.horizontal * 2 - - object.linesCopy = {} - for i = 1, #object.lines do - table.insert(object.linesCopy, object.lines[i]) - end - - if object.autoWrap then - object.linesCopy = string.wrap(object.linesCopy, object.textWidth) - end - - if object.autoHeight then - object.height = #object.linesCopy + doubleVerticalOffset - end - - object.textHeight = object.height - doubleVerticalOffset -end - -local function textBoxDraw(object) - textBoxCalculate(object) - - if object.colors.background then - buffer.square(object.x, object.y, object.width, object.height, object.colors.background, object.colors.text, " ", object.colors.transparency) - end - - local x, y = nil, object.y + object.offset.vertical - local lineType, text, textColor - for i = object.currentLine, object.currentLine + object.textHeight - 1 do - if object.linesCopy[i] then - lineType = type(object.linesCopy[i]) - if lineType == "string" then - text, textColor = string.limit(object.linesCopy[i], object.textWidth), object.colors.text - elseif lineType == "table" then - text, textColor = string.limit(object.linesCopy[i].text, object.textWidth), object.linesCopy[i].color - else - error("Unknown TextBox line type: " .. tostring(lineType)) - end - - x = GUI.getAlignmentCoordinates( - { - x = object.x + object.offset.horizontal, - y = 1, - width = object.textWidth, - height = 1, - alignment = object.alignment - }, - { - width = unicode.len(text), - height = 1 - } - ) - buffer.text(x, y, textColor, text) - y = y + 1 - else - break - end - end - - return object -end - -local function scrollDownTextBox(object, count) - count = count or 1 - local maxCountAvailableToScroll = #object.lines - object.height - object.currentLine + 1 - count = math.min(count, maxCountAvailableToScroll) - if #object.lines >= object.height and object.currentLine < #object.lines - count then - object.currentLine = object.currentLine + count - end - - return object -end - -local function scrollUpTextBox(object, count) - count = count or 1 - if object.currentLine > count and object.currentLine >= 1 then object.currentLine = object.currentLine - count end - return object -end - -local function scrollToStartTextBox(object) - object.currentLine = 1 - return object -end - -local function scrollToEndTextBox(object) - object.currentLine = #object.lines - return object -end - -local function textBoxScrollEventHandler(mainContainer, object, eventData) - if eventData[1] == "scroll" then - if eventData[5] == 1 then - object:scrollUp() - mainContainer:draw() - buffer.draw() - else - object:scrollDown() - mainContainer:draw() - buffer.draw() - end - end -end - -function GUI.textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset, autoWrap, autoHeight) - local object = GUI.object(x, y, width, height) - - object.eventHandler = textBoxScrollEventHandler - object.colors = { - text = textColor, - background = backgroundColor - } - object.setAlignment = GUI.setAlignment - object:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top) - object.lines = lines - object.currentLine = currentLine or 1 - object.draw = textBoxDraw - object.scrollUp = scrollUpTextBox - object.scrollDown = scrollDownTextBox - object.scrollToStart = scrollToStartTextBox - object.scrollToEnd = scrollToEndTextBox - object.offset = {horizontal = horizontalOffset or 0, vertical = verticalOffset or 0} - object.autoWrap = autoWrap - object.autoHeight = autoHeight - - textBoxCalculate(object) - - return object -end - ----------------------------------------- Horizontal Slider Object ----------------------------------------- local function sliderDraw(object) @@ -2911,7 +2779,7 @@ local function treeAddItem(tree, name, definition, offset, expandable, disabled) return tree end -function GUI.tree(x, y, width, height, backgroundColor, expandableColor, notExpandableColor, arrowColor, backgroundSelectionColor, anySelectionColor, arrowSelectionColor, disabledColor, scrollBarBackground, scrollBarForeground, showMode, selectionMode) +function GUI.tree(x, y, width, height, backgroundColor, expandableColor, notExpandableColor, arrowColor, backgroundSelectedColor, anySelectionColor, arrowSelectionColor, disabledColor, scrollBarBackground, scrollBarForeground, showMode, selectionMode) local tree = GUI.container(x, y, width, height) tree.eventHandler = treeEventHandler @@ -2923,7 +2791,7 @@ function GUI.tree(x, y, width, height, backgroundColor, expandableColor, notExpa arrow = arrowColor, }, selected = { - background = backgroundSelectionColor, + background = backgroundSelectedColor, any = anySelectionColor, arrow = arrowSelectionColor, }, @@ -3010,6 +2878,161 @@ function GUI.filesystemTree(...) return tree end +----------------------------------------- Text Box object ----------------------------------------- + +local function textBoxCalculate(object) + local doubleVerticalOffset = object.offset.vertical * 2 + object.textWidth = object.width - object.offset.horizontal * 2 - (object.scrollBarEnabled and 1 or 0) + + object.linesCopy = {} + + if object.autoWrap then + for i = 1, #object.lines do + local isTable = type(object.lines[i]) == "table" + for subLine in (isTable and object.lines[i].text or object.lines[i]):gmatch("[^\n]+") do + local wrappedLine = string.wrap(subLine, object.textWidth) + for j = 1, #wrappedLine do + table.insert(object.linesCopy, isTable and {text = wrappedLine[j], color = object.lines[i].color} or wrappedLine[j]) + end + end + end + else + for i = 1, #object.lines do + table.insert(object.linesCopy, object.lines[i]) + end + end + + if object.autoHeight then + object.height = #object.linesCopy + doubleVerticalOffset + end + + object.textHeight = object.height - doubleVerticalOffset +end + +local function textBoxDraw(object) + textBoxCalculate(object) + + if object.colors.background then + buffer.square(object.x, object.y, object.width, object.height, object.colors.background, object.colors.text, " ", object.colors.transparency) + end + + local x, y = nil, object.y + object.offset.vertical + local lineType, text, textColor + for i = object.currentLine, object.currentLine + object.textHeight - 1 do + if object.linesCopy[i] then + lineType = type(object.linesCopy[i]) + if lineType == "string" then + text, textColor = string.limit(object.linesCopy[i], object.textWidth), object.colors.text + elseif lineType == "table" then + text, textColor = string.limit(object.linesCopy[i].text, object.textWidth), object.linesCopy[i].color + else + error("Unknown TextBox line type: " .. tostring(lineType)) + end + + x = GUI.getAlignmentCoordinates( + { + x = object.x + object.offset.horizontal, + y = 1, + width = object.textWidth, + height = 1, + alignment = object.alignment + }, + { + width = unicode.len(text), + height = 1 + } + ) + buffer.text(x, y, textColor, text) + y = y + 1 + else + break + end + end + + if object.scrollBarEnabled and object.textHeight < #object.lines then + object.scrollBar.x = object.x + object.width - 1 + object.scrollBar.y = object.y + object.scrollBar.height = object.height + object.scrollBar.maximumValue = #object.lines - object.textHeight + 1 + object.scrollBar.value = object.currentLine + object.scrollBar.shownValueCount = object.textHeight + + object.scrollBar:draw() + end + + return object +end + +local function scrollDownTextBox(object, count) + count = math.min(count or 1, #object.lines - object.height - object.currentLine + object.offset.vertical * 2 + 1) + if #object.lines >= object.height and object.currentLine < #object.lines - count then + object.currentLine = object.currentLine + count + end + + return object +end + +local function scrollUpTextBox(object, count) + count = count or 1 + if object.currentLine > count and object.currentLine >= 1 then object.currentLine = object.currentLine - count end + return object +end + +local function scrollToStartTextBox(object) + object.currentLine = 1 + return object +end + +local function scrollToEndTextBox(object) + if #object.lines > object.textHeight then + object.currentLine = #object.lines - object.textHeight + 1 + end + + return object +end + +local function textBoxScrollEventHandler(mainContainer, object, eventData) + if eventData[1] == "scroll" then + if eventData[5] == 1 then + object:scrollUp() + mainContainer:draw() + buffer.draw() + else + object:scrollDown() + mainContainer:draw() + buffer.draw() + end + end +end + +function GUI.textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset, autoWrap, autoHeight) + local object = GUI.object(x, y, width, height) + + object.eventHandler = textBoxScrollEventHandler + object.colors = { + text = textColor, + background = backgroundColor + } + object.setAlignment = GUI.setAlignment + object:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top) + object.lines = lines + object.currentLine = currentLine or 1 + object.draw = textBoxDraw + object.scrollUp = scrollUpTextBox + object.scrollDown = scrollDownTextBox + object.scrollToStart = scrollToStartTextBox + object.scrollToEnd = scrollToEndTextBox + object.offset = {horizontal = horizontalOffset or 0, vertical = verticalOffset or 0} + object.autoWrap = autoWrap + object.autoHeight = autoHeight + object.scrollBar = GUI.scrollBar(1, 1, 1, 1, 0xC3C3C3, 0x444444, 1, 1, 1, 1, 1, true) + object.scrollBarEnabled = false + + textBoxCalculate(object) + + return object +end + ----------------------------------------- Input object ----------------------------------------- local function inputSetCursorPosition(input, newPosition) @@ -3065,6 +3088,17 @@ local function inputDraw(input) local background = buffer.rawGet(index) buffer.rawSet(index, background, input.colors.cursor, input.cursorSymbol) end + + if input.autoCompleteEnabled then + input.autoComplete.x = input.x + if input.autoCompleteVerticalAlignment == GUI.alignment.vertical.top then + input.autoComplete.y = input.y - input.autoComplete.height + else + input.autoComplete.y = input.y + input.height + end + input.autoComplete.width = input.width + input.autoComplete:draw() + end end local function inputEventHandler(mainContainer, input, mainEventData) @@ -3077,6 +3111,10 @@ local function inputEventHandler(mainContainer, input, mainEventData) input.cursorBlinkState = true input:setCursorPosition(unicode.len(input.text) + 1) + if input.autoCompleteEnabled then + input.autoCompleteMatchMethod() + end + mainContainer:draw() buffer.draw() @@ -3090,57 +3128,85 @@ local function inputEventHandler(mainContainer, input, mainEventData) input.cursorBlinkState = true mainContainer:draw() buffer.draw() + elseif input.autoComplete:isClicked(eventData[3], eventData[4]) then + input.autoComplete.eventHandler(mainContainer, input.autoComplete, eventData) else input.cursorBlinkState = false break end + elseif eventData[1] == "scroll" then + input.autoComplete.eventHandler(mainContainer, input.autoComplete, eventData) elseif eventData[1] == "key_down" then + -- Tab + if eventData[4] == 15 then + + -- Return - if eventData[4] == 28 then - if input.historyEnabled then - -- Очистка истории - for i = 1, (#input.history - input.historyLimit) do - table.remove(input.history, 1) + elseif eventData[4] == 28 then + if input.autoCompleteEnabled and input.autoComplete.itemCount > 0 then + input.autoComplete.eventHandler(mainContainer, input.autoComplete, eventData) + else + if input.historyEnabled then + -- Очистка истории + for i = 1, (#input.history - input.historyLimit) do + table.remove(input.history, 1) + end + + -- Добавление введенных данных в историю + if input.history[#input.history] ~= input.text and unicode.len(input.text) > 0 then + table.insert(input.history, input.text) + end + input.historyIndex = #input.history end - -- Добавление введенных данных в историю - if input.history[#input.history] ~= input.text and unicode.len(input.text) > 0 then - table.insert(input.history, input.text) - end - input.historyIndex = #input.history + input.cursorBlinkState = false + break end - - input.cursorBlinkState = false - break -- Arrows up/down/left/right - elseif eventData[4] == 200 and input.historyEnabled then - if #input.history > 0 then - -- Добавление уже введенного текста в историю при стрелке вверх - if input.historyIndex == #input.history + 1 and unicode.len(input.text) > 0 then - table.insert(input.history, input.text) - end + elseif eventData[4] == 200 then + if input.autoCompleteEnabled and input.autoComplete.selectedItem > 1 then + input.autoComplete.eventHandler(mainContainer, input.autoComplete, eventData) + else + if input.historyEnabled and #input.history > 0 then + -- Добавление уже введенного текста в историю при стрелке вверх + if input.historyIndex == #input.history + 1 and unicode.len(input.text) > 0 then + input.history[input.historyIndex] = input.text + end - input.historyIndex = input.historyIndex - 1 - if input.historyIndex > #input.history then - input.historyIndex = #input.history - elseif input.historyIndex < 1 then - input.historyIndex = 1 - end + input.historyIndex = input.historyIndex - 1 + if input.historyIndex > #input.history then + input.historyIndex = #input.history + elseif input.historyIndex < 1 then + input.historyIndex = 1 + end - input.text = input.history[input.historyIndex] - input:setCursorPosition(unicode.len(input.text) + 1) + input.text = input.history[input.historyIndex] + input:setCursorPosition(unicode.len(input.text) + 1) + + if input.autoCompleteEnabled then + input.autoCompleteMatchMethod() + end + end end - elseif eventData[4] == 208 and input.historyEnabled then - if #input.history > 0 then - input.historyIndex = input.historyIndex + 1 - if input.historyIndex > #input.history then - input.historyIndex = #input.history - elseif input.historyIndex < 1 then - input.historyIndex = 1 + elseif eventData[4] == 208 then + if input.autoCompleteEnabled and input.historyIndex == #input.history + 1 then + input.autoComplete.eventHandler(mainContainer, input.autoComplete, eventData) + else + if input.historyEnabled and #input.history > 0 then + input.historyIndex = input.historyIndex + 1 + if input.historyIndex > #input.history then + input.historyIndex = #input.history + elseif input.historyIndex < 1 then + input.historyIndex = 1 + end + + input.text = input.history[input.historyIndex] + input:setCursorPosition(unicode.len(input.text) + 1) + + if input.autoCompleteEnabled then + input.autoCompleteMatchMethod() + end end - - input.text = input.history[input.historyIndex] - input:setCursorPosition(unicode.len(input.text) + 1) end elseif eventData[4] == 203 then input:setCursorPosition(input.cursorPosition - 1) @@ -3150,14 +3216,26 @@ local function inputEventHandler(mainContainer, input, mainEventData) elseif eventData[4] == 14 then input.text = unicode.sub(unicode.sub(input.text, 1, input.cursorPosition - 1), 1, -2) .. unicode.sub(input.text, input.cursorPosition, -1) input:setCursorPosition(input.cursorPosition - 1) + + if input.autoCompleteEnabled then + input.autoCompleteMatchMethod() + end -- Delete elseif eventData[4] == 211 then input.text = unicode.sub(input.text, 1, input.cursorPosition - 1) .. unicode.sub(input.text, input.cursorPosition + 1, -1) + + if input.autoCompleteEnabled then + input.autoCompleteMatchMethod() + end else local char = unicode.char(eventData[3]) if not keyboard.isControl(eventData[3]) then input.text = unicode.sub(input.text, 1, input.cursorPosition - 1) .. char .. unicode.sub(input.text, input.cursorPosition, -1) input:setCursorPosition(input.cursorPosition + 1) + + if input.autoCompleteEnabled then + input.autoCompleteMatchMethod() + end end end @@ -3179,6 +3257,9 @@ local function inputEventHandler(mainContainer, input, mainEventData) end input.focused = false + if input.autoCompleteEnabled then + input.autoComplete:clear() + end GUI.callMethod(input.onInputFinished, mainContainer, input, mainEventData, input.text) @@ -3217,6 +3298,8 @@ function GUI.input(x, y, width, height, backgroundColor, textColor, placeholderT input.textMask = textMask input.setCursorPosition = inputSetCursorPosition + + input.history = {} input.historyLimit = 20 input.historyIndex = 0 @@ -3226,11 +3309,174 @@ function GUI.input(x, y, width, height, backgroundColor, textColor, placeholderT input.draw = inputDraw input.eventHandler = inputEventHandler + input.autoComplete = GUI.autoComplete(1, 1, 30, 7, 0xE1E1E1, 0x999999, 0x3C3C3C, 0x3C3C3C, 0x999999, 0xE1E1E1, 0xC3C3C3, 0x444444) + input.autoCompleteEnabled = false + input.autoCompleteVerticalAlignment = GUI.alignment.vertical.bottom + return input end -------------------------------------------------------------------------------------------------------------------------------- +local function autoCompleteDraw(object) + local y, yEnd = object.y, object.y + object.height - 1 + + buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ") + + for i = object.fromItem, object.itemCount do + local textColor, textMatchColor = object.colors.default.text, object.colors.default.textMatch + if i == object.selectedItem then + buffer.square(object.x, y, object.width, 1, object.colors.selected.background, object.colors.selected.text, " ") + textColor, textMatchColor = object.colors.selected.text, object.colors.selected.textMatch + end + + buffer.text(object.x + 1, y, textMatchColor, object.matchText) + buffer.text(object.x + 1 + object.matchTextLength, y, textColor, unicode.sub(object.items[i], object.matchTextLength + 1, object.width - 2 - object.matchTextLength)) + + y = y + 1 + if y > yEnd then + break + end + end + + if object.itemCount > object.height then + object.scrollBar.x = object.x + object.width - 1 + object.scrollBar.y = object.y + object.scrollBar.height = object.height + object.scrollBar.maximumValue = object.itemCount - object.height + 1 + object.scrollBar.value = object.fromItem + object.scrollBar.shownValueCount = object.height + + object.scrollBar:draw() + end +end + +local function autoCompleteScroll(mainContainer, object, direction) + if object.itemCount >= object.height then + object.fromItem = object.fromItem + direction + if object.fromItem < 1 then + object.fromItem = 1 + elseif object.fromItem > object.itemCount - object.height + 1 then + object.fromItem = object.itemCount - object.height + 1 + end + end +end + +local function autoCompleteEventHandler(mainContainer, object, eventData) + if eventData[1] == "touch" then + object.selectedItem = eventData[4] - object.y + object.fromItem + mainContainer:draw() + buffer.draw() + + GUI.callMethod(object.onItemSelected, mainContainer, object, eventData, object.selectedItem) + elseif eventData[1] == "scroll" then + autoCompleteScroll(mainContainer, object, -eventData[5]) + mainContainer:draw() + buffer.draw() + elseif eventData[1] == "key_down" then + if eventData[4] == 28 then + GUI.callMethod(object.onItemSelected, mainContainer, object, eventData, object.selectedItem) + elseif eventData[4] == 200 then + object.selectedItem = object.selectedItem - 1 + if object.selectedItem < 1 then + object.selectedItem = 1 + end + + if object.selectedItem == object.fromItem - 1 then + autoCompleteScroll(mainContainer, object, -1) + end + + mainContainer:draw() + buffer.draw() + elseif eventData[4] == 208 then + object.selectedItem = object.selectedItem + 1 + if object.selectedItem > object.itemCount then + object.selectedItem = object.itemCount + end + + if object.selectedItem == object.fromItem + object.height then + autoCompleteScroll(mainContainer, object, 1) + end + + mainContainer:draw() + buffer.draw() + end + end +end + +local function autoCompleteClear(object) + object.items = {} + object.itemCount = 0 + object.fromItem = 1 + object.selectedItem = 1 + object.height = 0 +end + +local function autoCompleteMatch(object, variants, text) + object:clear() + + if text then + object.matchText = text + object.matchTextLength = unicode.len(text) + for i = 1, #variants do + if variants[i] ~= text and variants[i]:match("^" .. text) then + table.insert(object.items, variants[i]) + end + end + else + object.matchText = "" + object.matchTextLength = 0 + for i = 1, #variants do + table.insert(object.items, variants[i]) + end + end + + table.sort(object.items, function(a, b) return unicode.lower(a) < unicode.lower(b) end) + + object.itemCount = #object.items + object.height = math.min(object.itemCount, object.maximumHeight) + + return object +end + +function GUI.autoComplete(x, y, width, maximumHeight, backgroundColor, textColor, textMatchColor, backgroundSelectedColor, textSelectedColor, textMatchSelectedColor, scrollBarBackground, scrollBarForeground) + local object = GUI.object(x, y, width, maximumHeight) + + object.colors = { + default = { + background = backgroundColor, + text = textColor, + textMatch = textMatchColor + }, + selected = { + background = backgroundSelectedColor, + text = textSelectedColor, + textMatch = textMatchSelectedColor + } + } + + object.maximumHeight = maximumHeight + object.fromItem = 1 + object.selectedItem = 1 + object.items = {} + object.matchText = " " + object.matchTextLength = 1 + object.itemCount = 0 + + object.scrollBar = GUI.scrollBar(1, 1, 1, 1, scrollBarBackground, scrollBarForeground, 1, 1, 1, 1, 1, true) + + object.match = autoCompleteMatch + object.draw = autoCompleteDraw + object.eventHandler = autoCompleteEventHandler + object.clear = autoCompleteClear + + object:clear() + + return object +end + +-------------------------------------------------------------------------------------------------------------------------------- + -- buffer.flush() -- buffer.draw(true) @@ -3239,12 +3485,94 @@ end -- local mainContainer = GUI.fullScreenContainer() -- mainContainer:addChild(GUI.panel(1, 1, mainContainer.width, mainContainer.height, 0x2D2D2D)) --- local input = mainContainer:addChild(GUI.input(2, 2, 100, 3, 0x3C3C3C, 0xAAAAAA, 0x555555, 0x444444, 0xE1E1E1, "", "Enter command here", true)) +-- local input = mainContainer:addChild(GUI.input(3, 2, 30, 3, 0xFFFFFF, 0x555555, 0x888888, 0xFFFFFF, 0x777777, "", "Placeholder")) + +-- input.autoCompleteEnabled = true +-- input.autoCompleteMatchMethod = function() +-- local inputTextLength = unicode.len(input.text) + +-- local left, right = 1, inputTextLength +-- for i = input.cursorPosition - 1, 1, -1 do +-- if unicode.sub(input.text, i, i) == " " then +-- left = i + 1 +-- break +-- end +-- end +-- for i = input.cursorPosition, inputTextLength do +-- if unicode.sub(input.text, i, i) == " " then +-- right = i - 1 +-- break +-- end +-- end +-- local cykaText = unicode.sub(input.text, left, right) + +-- if cykaText:match("^[%w%.]+$") then +-- local array = {} +-- for word in cykaText:gmatch("[^%.]+") do +-- table.insert(array, word) +-- end + +-- local t = _G +-- for i = 1, #array - 1 do +-- if t[array[i]] and type(t[array[i]]) == "table" then +-- t = t[array[i]] +-- else +-- input.autoComplete:clear() +-- return +-- end +-- end + +-- input.autoComplete.result = unicode.sub(input.text, 1, left - 1) +-- if #array - 1 > 0 then +-- input.autoComplete.result = input.autoComplete.result .. table.concat(array, ".", 1, #array - 1) .. "." +-- end + +-- local lastWord = array[#array] +-- array = {} +-- for key, value in pairs(t) do +-- table.insert(array, tostring(key)) +-- end +-- input.autoComplete:match(array, lastWord) +-- end +-- end + +-- input.autoComplete.onItemSelected = function() +-- input.text = input.autoComplete.result .. input.autoComplete.items[input.autoComplete.selectedItem] +-- input:setCursorPosition(unicode.len(input.text) + 1) + +-- if input.autoCompleteEnabled then +-- input.autoCompleteMatchMethod() +-- end + +-- mainContainer:draw() +-- buffer.draw() +-- end -- input.onInputFinished = function() -- input.text = "" +-- input.textCutFrom = 1 +-- input:setCursorPosition(1) + +-- mainContainer:draw() +-- buffer.draw() -- end +-- -- local textBox = mainContainer:addChild(GUI.textBox(2, 2, 40, 16, 0xEEEEEE, 0x2D2D2D, {}, 1, 2, 1, true)) +-- -- table.insert(textBox.lines, {text = "Sample colored line ", color = 0x880000}) + +-- -- for i = 1, 30 do +-- -- table.insert(textBox.lines, "cyak " .. i) +-- -- end + +-- -- local file = io.open("/lib/doubleBuffering.lua") +-- -- for line in file:lines() do +-- -- line = line:gsub("\t", " "):gsub("\r\n", "\n") +-- -- table.insert(textBox.lines, line) +-- -- end +-- -- file:close() + +-- -- textBox:scrollToEnd() + -- ------------------------------------------------------------------------------------------ -- mainContainer:draw() diff --git a/lib/advancedLua.lua b/lib/advancedLua.lua index 53bbf351..c763e07e 100755 --- a/lib/advancedLua.lua +++ b/lib/advancedLua.lua @@ -287,9 +287,9 @@ local function doSerialize(array, prettyLook, indentationSymbol, indentationSymb table.insert(text, equalsSymbol) - if valueType == "number" or valueType == "boolean" or valueType == "nil" or valueType == "function" then + if valueType == "number" or valueType == "boolean" or valueType == "nil" then table.insert(text, stringValue) - elseif valueType == "string"then + elseif valueType == "string" or valueType == "function" then table.insert(text, "\"") table.insert(text, stringValue) table.insert(text, "\"") @@ -310,7 +310,7 @@ local function doSerialize(array, prettyLook, indentationSymbol, indentationSymb ) ) else - table.insert(text, "…") + table.insert(text, "\"…\"") end end