----------------------------------------- Libraries ----------------------------------------- local libraries = { advancedLua = "advancedLua", buffer = "doubleBuffering", unicode = "unicode", event = "event", } for library in pairs(libraries) do if not _G[library] then _G[library] = require(libraries[library]) end end; libraries = nil ----------------------------------------- Core constants ----------------------------------------- local GUI = {} GUI.alignment = { horizontal = enum( "left", "center", "right" ), vertical = enum( "top", "center", "bottom" ) } GUI.directions = enum( "horizontal", "vertical" ) GUI.colors = { disabled = { background = 0x888888, text = 0xAAAAAA }, contextMenu = { separator = 0xAAAAAA, default = { background = 0xFFFFFF, text = 0x2D2D2D }, disabled = { text = 0xAAAAAA }, pressed = { background = 0x3366CC, text = 0xFFFFFF }, transparency = { background = 20, shadow = 50 } } } GUI.dropDownMenuElementTypes = enum( "default", "separator" ) GUI.objectTypes = enum( "empty", "panel", "label", "button", "framedButton", "image", "windowActionButtons", "windowActionButton", "tabBar", "tabBarTab", "menu", "menuElement", "window", "inputTextBox", "textBox", "horizontalSlider", "switch", "progressBar", "chart", "comboBox" ) ----------------------------------------- Primitive objects ----------------------------------------- -- Universal method to check if object was clicked by following coordinates local function isObjectClicked(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 and not object.hidden then return true end return false end -- Limit object's text field to its' size local function objectTextLimit(object) local text, textLength = object.text, unicode.len(object.text) if textLength > object.width then text = unicode.sub(text, 1, object.width); textLength = object.width end return text, textLength end -- Base object to use in everything function GUI.object(x, y, width, height) return { x = x, y = y, width = width, height = height, isClicked = isObjectClicked } end ----------------------------------------- Object alignment ----------------------------------------- -- Set children alignment in parent object function GUI.setAlignment(object, horizontalAlignment, verticalAlignment) object.alignment = { horizontal = horizontalAlignment, vertical = verticalAlignment } return object end -- Get subObject position inside of parent object function GUI.getAlignmentCoordinates(object, subObject) local x, y if object.alignment.horizontal == GUI.alignment.horizontal.left then x = object.x elseif object.alignment.horizontal == GUI.alignment.horizontal.center then x = math.floor(object.x + object.width / 2 - subObject.width / 2) elseif object.alignment.horizontal == GUI.alignment.horizontal.right then x = object.x + object.width - subObject.width else error("Unknown horizontal alignment: " .. tostring(object.alignment.horizontal)) end if object.alignment.vertical == GUI.alignment.vertical.top then y = object.y elseif object.alignment.vertical == GUI.alignment.vertical.center then y = math.floor(object.y + object.height / 2 - subObject.height / 2) elseif object.alignment.vertical == GUI.alignment.vertical.bottom then y = object.y + object.height - subObject.height else error("Unknown vertical alignment: " .. tostring(object.alignment.vertical)) end return x, y end ----------------------------------------- Containers ----------------------------------------- -- Object calling by it's name using 'container["objectName"]' against of 'container.children[1, 2, 3, ...]' function GUI.setContainerMetasearch(container) setmetatable(container, { __index = function(container, objectName) for objectIndex = 1, #container.children do if container.children[objectIndex].name == objectName then return container.children[objectIndex] end end end }) end -- Go recursively through every container's object (including other containers) and return object that was clicked firstly by it's GUI-layer position function GUI.getClickedObject(container, xEvent, yEvent) local clickedObject, clickedIndex for objectIndex = #container.children, 1, -1 do if not container.children[objectIndex].hidden then container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1 if container.children[objectIndex].children and #container.children[objectIndex].children > 0 then clickedObject, clickedIndex = GUI.getClickedObject(container.children[objectIndex], xEvent, yEvent) if clickedObject then break end elseif not container.children[objectIndex].disableClicking and container.children[objectIndex]:isClicked(xEvent, yEvent) then clickedObject, clickedIndex = container.children[objectIndex], objectIndex break end end end return clickedObject, clickedIndex end local function checkObjectParentExists(object) if not object.parent then error("Object doesn't have a parent container") end end local function containerObjectIndexOf(object) checkObjectParentExists(object) for objectIndex = 1, #object.parent.children do if object.parent.children[objectIndex] == object then return objectIndex end end end -- Move container's object "closer" to our eyes local function containerObjectMoveForward(object) local objectIndex = object:indexOf() if objectIndex < #object.parent.children then object.parent.children[index], object.parent.children[index + 1] = swap(object.parent.children[index], object.parent.children[index + 1]) end end -- Move container's object "more far out" of our eyes local function containerObjectMoveBackward(object) local objectIndex = object:indexOf() if objectIndex > 1 then object.parent.children[index], object.parent.children[index - 1] = swap(object.parent.children[index], object.parent.children[index - 1]) end end -- Move container's object to front of all objects local function containerObjectMoveToFront(object) local objectIndex = object:indexOf() table.insert(object.parent.children, object) table.remove(object.parent.children, objectIndex) end -- Move container's object to back of all objects local function containerObjectMoveToBack(object) local objectIndex = object:indexOf() table.insert(object.parent.children, 1, object) table.remove(object.parent.children, objectIndex + 1) end -- Add any object as children to parent container with specified objectName local function addObjectToContainer(container, objectType, objectName, object) object.name = objectName object.type = objectType object.parent = container object.indexOf = containerObjectIndexOf object.moveToFront = containerObjectMoveToFront object.moveToBack = containerObjectMoveToBack object.moveForward = containerObjectMoveForward object.moveBackward = containerObjectMoveBackward object.localPosition = {x = object.x, y = object.y} table.insert(container.children, object) return object end -- Add empty GUI.object to container local function addEmptyObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.empty, objectName, GUI.object(...)) end -- Add button object to container local function addButtonObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.button, objectName, GUI.button(...)) end -- Add adaptive button object to container local function addAdaptiveButtonObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.button, objectName, GUI.adaptiveButton(...)) end -- Add framedButton object to container local function addFramedButtonObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.button, objectName, GUI.framedButton(...)) end -- Add adaptive framedButton object to container local function addAdaptiveFramedButtonObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.button, objectName, GUI.adaptiveFramedButton(...)) end -- Add label object to container local function addLabelObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.label, objectName, GUI.label(...)) end -- Add panel object to container local function addPanelObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.panel, objectName, GUI.panel(...)) end -- Add windowActionButtons object to container local function addWindowActionButtonsObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.windowActionButtons, objectName, GUI.windowActionButtons(...)) end -- Add another container to container local function addContainerToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.container, objectName, GUI.container(...)) end -- Add image object to container local function addImageObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.image, objectName, GUI.image(...)) end -- Add image object to container local function addTabBarObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.tabBar, objectName, GUI.tabBar(...)) end -- Add InputTextBox object to container local function addInputTextBoxObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.inputTextBox, objectName, GUI.inputTextBox(...)) end -- Add TextBox object to container local function addTextBoxObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.textBox, objectName, GUI.textBox(...)) end -- Add Horizontal Slider object to container local function addHorizontalSliderObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.horizontalSlider, objectName, GUI.horizontalSlider(...)) end -- Add Progressbar object to container local function addProgressBarObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.progressBar, objectName, GUI.progressBar(...)) end -- Add Switch object to container local function addSwitchObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.switch, objectName, GUI.switch(...)) end -- Add Chart object to container local function addChartObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.chart, objectName, GUI.chart(...)) end -- Add ComboBox object to container local function addComboBoxObjectToContainer(container, objectName, ...) return addObjectToContainer(container, GUI.objectTypes.comboBox, objectName, GUI.comboBox(...)) end -- Recursively draw container's content including all children container's content local function drawContainerContent(container) for objectIndex = 1, #container.children do if not container.children[objectIndex].hidden then container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1 if container.children[objectIndex].children then -- drawContainerContent(container.children[objectIndex]) -- We use :draw() method against of recursive call. The reason is possible user-defined :draw() reimplementations container.children[objectIndex]:draw() else if container.children[objectIndex].draw then container.children[objectIndex]:draw() else error("Container object with index " .. objectIndex .. " and name \"" .. tostring(container.children[objectIndex].name) .. "\" doesn't have :draw() method") end end end end return container end -- Delete every container's children object local function deleteContainersContent(container) for objectIndex = 1, #container.children do container.children[objectIndex] = nil end end -- Universal container to store any other objects like buttons, labels, etc function GUI.container(x, y, width, height) local container = GUI.object(x, y, width, height) container.children = {} GUI.setContainerMetasearch(container) container.draw = drawContainerContent container.getClickedObject = GUI.getClickedObject container.deleteObjects = deleteContainersContent container.addObject = addEmptyObjectToContainer container.addContainer = addContainerToContainer container.addPanel = addPanelObjectToContainer container.addLabel = addLabelObjectToContainer container.addButton = addButtonObjectToContainer container.addAdaptiveButton = addAdaptiveButtonObjectToContainer container.addFramedButton = addFramedButtonObjectToContainer container.addAdaptiveFramedButton = addAdaptiveFramedButtonObjectToContainer container.addWindowActionButtons = addWindowActionButtonsObjectToContainer container.addImage = addImageObjectToContainer container.addTabBar = addTabBarObjectToContainer container.addTextBox = addTextBoxObjectToContainer container.addInputTextBox = addInputTextBoxObjectToContainer container.addHorizontalSlider = addHorizontalSliderObjectToContainer container.addSwitch = addSwitchObjectToContainer container.addProgressBar = addProgressBarObjectToContainer container.addChart = addChartObjectToContainer container.addComboBox = addComboBoxObjectToContainer return container end ----------------------------------------- Buttons ----------------------------------------- local function drawButton(object) local text, textLength = objectTextLimit(object) local xText, yText = GUI.getAlignmentCoordinates(object, {width = textLength, height = 1}) local buttonColor = object.disabled and object.colors.disabled.background or (object.pressed and object.colors.pressed.background or object.colors.default.background) local textColor = object.disabled and object.colors.disabled.text or (object.pressed and object.colors.pressed.text or object.colors.default.text) if buttonColor then if object.buttonType == GUI.objectTypes.button then buffer.square(object.x, object.y, object.width, object.height, buttonColor, textColor, " ") else buffer.frame(object.x, object.y, object.width, object.height, buttonColor) end end buffer.text(xText, yText, textColor, text) return object end local function pressButton(object) object.pressed = true drawButton(object) end local function releaseButton(object) object.pressed = nil drawButton(object) end local function pressAndReleaseButton(object, pressTime) pressButton(object) buffer.draw() os.sleep(pressTime or 0.2) releaseButton(object) buffer.draw() end -- Создание таблицы кнопки со всеми необходимыми параметрами local function createButtonObject(buttonType, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) local object = GUI.object(x, y, width, height) object.colors = { default = { background = buttonColor, text = textColor }, pressed = { background = buttonPressedColor, text = textPressedColor }, disabled = { background = GUI.colors.disabled.background, text = GUI.colors.disabled.text, } } object.buttonType = buttonType object.disabled = disabledState object.text = text object.press = pressButton object.release = releaseButton object.pressAndRelease = pressAndReleaseButton object.draw = drawButton object.setAlignment = GUI.setAlignment object:setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.center) return object end -- Кнопка фиксированных размеров function GUI.button(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) return createButtonObject(GUI.objectTypes.button, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) end -- Кнопка, подстраивающаяся под размер текста function GUI.adaptiveButton(x, y, xOffset, yOffset, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) return createButtonObject(GUI.objectTypes.button, x, y, unicode.len(text) + xOffset * 2, yOffset * 2 + 1, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) end -- Кнопка в рамке function GUI.framedButton(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) return createButtonObject(GUI.objectTypes.framedButton, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) end function GUI.adaptiveFramedButton(x, y, xOffset, yOffset, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) return createButtonObject(GUI.objectTypes.framedButton, x, y, unicode.len(text) + xOffset * 2, yOffset * 2 + 1, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState) end ----------------------------------------- TabBar ----------------------------------------- local function drawTabBar(object) for tab = 1, #object.tabs.children do if tab == object.selectedTab then object.tabs.children[tab].pressed = true else object.tabs.children[tab].pressed = false end end object:reimplementedDraw() return object end function GUI.tabBar(x, y, width, height, spaceBetweenElements, backgroundColor, textColor, backgroundSelectedColor, textSelectedColor, ...) local elements, object = {...}, GUI.container(x, y, width, height) object.selectedTab = 1 object.tabsWidth = 0; for elementIndex = 1, #elements do object.tabsWidth = object.tabsWidth + unicode.len(elements[elementIndex]) + 2 + spaceBetweenElements end; object.tabsWidth = object.tabsWidth - spaceBetweenElements object.reimplementedDraw = object.draw object.draw = drawTabBar object:addPanel("backgroundPanel", 1, 1, object.width, object.height, backgroundColor).disableClicking = true object.tabs = object:addContainer("tabs", 1, 1, object.width, object.height) x = math.floor(width / 2 - object.tabsWidth / 2) for elementIndex = 1, #elements do local tab = object.tabs:addButton(elements[elementIndex], x, 1, unicode.len(elements[elementIndex]) + 2, height, backgroundColor, textColor, backgroundSelectedColor, textSelectedColor, elements[elementIndex]) tab.type = GUI.objectTypes.tabBarTab x = x + tab.width + spaceBetweenElements end return object end ----------------------------------------- Panel ----------------------------------------- local function drawPanel(object) buffer.square(object.x, object.y, object.width, object.height, object.colors.background, 0x000000, " ", object.colors.transparency) return object end function GUI.panel(x, y, width, height, color, transparency) local object = GUI.object(x, y, width, height) object.colors = {background = color, transparency = transparency} object.draw = drawPanel return object end ----------------------------------------- Label ----------------------------------------- local function drawLabel(object) local text, textLength = objectTextLimit(object) local xText, yText = GUI.getAlignmentCoordinates(object, {width = textLength, height = 1}) buffer.text(xText, yText, object.colors.text, text) return object end function GUI.label(x, y, width, height, textColor, text) local object = GUI.object(x, y, width, height) object.setAlignment = GUI.setAlignment object:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top) object.colors = {text = textColor} object.text = text object.draw = drawLabel return object end ----------------------------------------- Image ----------------------------------------- local function drawImage(object) buffer.image(object.x, object.y, object.image) return object end function GUI.image(x, y, image) local object = GUI.object(x, y, image.width, image.height) object.image = image object.draw = drawImage return object end ----------------------------------------- Window action buttons ----------------------------------------- local function drawWindowActionButton(object) local background = buffer.get(object.x, object.y) object.colors.default.background, object.colors.pressed.background, object.colors.disabled.background = background, background, background object:reimplementedDraw() return object end function GUI.windowActionButtons(x, y, fatSymbol) local symbol = fatSymbol and "⬤" or "●" local container = GUI.container(x, y, 5, 1) local closeButton = container:addButton("close", 1, 1, 1, 1, 0x000000, 0xFF4940, 0x000000, 0x992400, symbol) local minimizeButton = container:addButton("minimize", 3, 1, 1, 1, 0x000000, 0xFFB640, 0x000000, 0x996D00, symbol) local maximizeButton = container:addButton("maximize", 5, 1, 1, 1, 0x000000, 0x00B640, 0x000000, 0x006D40, symbol) closeButton.reimplementedDraw, minimizeButton.reimplementedDraw, maximizeButton.reimplementedDraw = closeButton.draw, minimizeButton.draw, maximizeButton.draw closeButton.draw, minimizeButton.draw, maximizeButton.draw = drawWindowActionButton, drawWindowActionButton, drawWindowActionButton return container end ----------------------------------------- Dropdown Menu ----------------------------------------- local function drawDropDownMenuElement(object, itemIndex, isPressed) local y = object.y + itemIndex * (object.spaceBetweenElements + 1) - 1 local yText = math.floor(y) if object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then local textColor = object.items[itemIndex].disabled and object.colors.disabled.text or (object.items[itemIndex].color or object.colors.default.text) -- Нажатие if isPressed then buffer.square(object.x, y - object.spaceBetweenElements, object.width, object.spaceBetweenElements * 2 + 1, object.colors.pressed.background, object.colors.pressed.text, " ") textColor = object.colors.pressed.text end -- Основной текст buffer.text(object.x + object.sidesOffset, yText, textColor, string.limit(object.items[itemIndex].text, object.width - object.sidesOffset * 2, false)) -- Шурткатикус if object.items[itemIndex].shortcut then buffer.text(object.x + object.width - unicode.len(object.items[itemIndex].shortcut) - object.sidesOffset, yText, textColor, object.items[itemIndex].shortcut) end else -- Сепаратор buffer.text(object.x, yText, object.colors.separator, string.rep("─", object.width)) end end local function drawDropDownMenu(object) buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ", object.colors.transparency) if object.drawShadow then GUI.windowShadow(object.x, object.y, object.width, object.height, GUI.colors.contextMenu.transparency.shadow, true) end for itemIndex = 1, #object.items do drawDropDownMenuElement(object, itemIndex, false) end end local function showDropDownMenu(object) local oldDrawLimit = buffer.getDrawLimit(); buffer.resetDrawLimit() object.height = #object.items * (object.spaceBetweenElements + 1) + object.spaceBetweenElements local oldPixels = buffer.copy(object.x, object.y, object.width + 1, object.height + 1) local function quit() buffer.paste(object.x, object.y, oldPixels) buffer.draw() buffer.setDrawLimit(oldDrawLimit) end drawDropDownMenu(object) buffer.draw() while true do local e = {event.pull()} if e[1] == "touch" then local objectFound = false for itemIndex = 1, #object.items do if e[3] >= object.x and e[3] <= object.x + object.width - 1 and e[4] == object.y + itemIndex * (object.spaceBetweenElements + 1) - 1 then objectFound = true if not object.items[itemIndex].disabled and object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then drawDropDownMenuElement(object, itemIndex, true) buffer.draw() os.sleep(0.2) quit() return object.items[itemIndex].text, itemIndex end break end end if not objectFound then quit(); return end end end end local function addDropDownMenuItem(object, text, disabled, shortcut, color) local item = {} item.type = GUI.dropDownMenuElementTypes.default item.text = text item.disabled = disabled item.shortcut = shortcut item.color = color table.insert(object.items, item) return item end local function addDropDownMenuSeparator(object) local item = {type = GUI.dropDownMenuElementTypes.separator} table.insert(object.items, item) return item end function GUI.dropDownMenu(x, y, width, spaceBetweenElements, backgroundColor, textColor, backgroundPressedColor, textPressedColor, disabledColor, separatorColor, transparency, items) local object = GUI.object(x, y, width, 1) object.colors = { default = { background = backgroundColor, text = textColor }, pressed = { background = backgroundPressedColor, text = textPressedColor }, disabled = { text = disabledColor }, separator = separatorColor, transparency = transparency } object.sidesOffset = 2 object.spaceBetweenElements = spaceBetweenElements object.addSeparator = addDropDownMenuSeparator object.addItem = addDropDownMenuItem object.items = {} if items then for i = 1, #items do object:addItem(items[i]) end end object.drawShadow = true object.draw = drawDropDownMenu object.show = showDropDownMenu return object end ----------------------------------------- Context Menu ----------------------------------------- local function showContextMenu(object) -- Расчет ширины окна меню local longestItem, longestShortcut = 0, 0 for itemIndex = 1, #object.items do if object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then longestItem = math.max(longestItem, unicode.len(object.items[itemIndex].text)) if object.items[itemIndex].shortcut then longestShortcut = math.max(longestShortcut, unicode.len(object.items[itemIndex].shortcut)) end end end object.width = object.sidesOffset + longestItem + (longestShortcut > 0 and 3 + longestShortcut or 0) + object.sidesOffset object.height = #object.items * (object.spaceBetweenElements + 1) + object.spaceBetweenElements -- А это чтоб за края экрана не лезло if object.y + object.height >= buffer.screen.height then object.y = buffer.screen.height - object.height end if object.x + object.width + 1 >= buffer.screen.width then object.x = buffer.screen.width - object.width - 1 end object:reimplementedShow() end function GUI.contextMenu(x, y, ...) local argumentItems = {...} local object = GUI.dropDownMenu(x, y, 1, 0, GUI.colors.contextMenu.default.background, GUI.colors.contextMenu.default.text, GUI.colors.contextMenu.pressed.background, GUI.colors.contextMenu.pressed.text, GUI.colors.contextMenu.disabled.text, GUI.colors.contextMenu.separator, GUI.colors.contextMenu.transparency.background) -- Заполняем менюшку парашей for itemIndex = 1, #argumentItems do if argumentItems[itemIndex] == "-" then object:addSeparator() else object:addItem(argumentItems[itemIndex][1], argumentItems[itemIndex][2], argumentItems[itemIndex][3], argumentItems[itemIndex][4]) end end object.reimplementedShow = object.show object.show = showContextMenu object.selectedElement = nil object.spaceBetweenElements = 0 return object end ----------------------------------------- Menu ----------------------------------------- function GUI.menu(x, y, width, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundTransparency, ...) local elements = {...} local menuObject = GUI.container(x, y, width, 1) menuObject:addPanel("backgroundPanel", 1, 1, menuObject.width, 1, backgroundColor, backgroundTransparency).disableClicking = true local x = 2 for elementIndex = 1, #elements do local button = menuObject:addAdaptiveButton(elementIndex, x, 1, 1, 0, nil, elements[elementIndex][2] or textColor, elements[elementIndex][3] or backgroundPressedColor, elements[elementIndex][4] or textPressedColor, elements[elementIndex][1]) button.type = GUI.objectTypes.menuElement x = x + button.width end return menuObject end ----------------------------------------- ProgressBar Object ----------------------------------------- local function drawProgressBar(object) local activeWidth = math.floor(object.value * object.width / 100) if object.thin then buffer.text(object.x, object.y, object.colors.passive, string.rep("━", object.width)) buffer.text(object.x, object.y, object.colors.active, string.rep("━", activeWidth)) else buffer.square(object.x, object.y, object.width, object.height, object.colors.passive) buffer.square(object.x, object.y, activeWidth, object.height, object.colors.active) end if object.showValue then local stringValue = tostring((object.valuePrefix or "") .. object.value .. (object.valuePostfix or "")) buffer.text(math.floor(object.x + object.width / 2 - unicode.len(stringValue) / 2), object.y + 1, object.colors.value, stringValue) end return object end function GUI.progressBar(x, y, width, activeColor, passiveColor, valueColor, value, thin, showValue, valuePrefix, valuePostfix) local object = GUI.object(x, y, width, 1) object.value = value object.colors = {active = activeColor, passive = passiveColor, value = valueColor} object.thin = thin object.draw = drawProgressBar object.showValue = showValue object.valuePrefix = valuePrefix object.valuePostfix = valuePostfix return object end ----------------------------------------- Other GUI elements ----------------------------------------- function GUI.windowShadow(x, y, width, height, transparency, thin) transparency = transparency or 50 if thin then buffer.square(x + width, y + 1, 1, height - 1, 0x000000, 0x000000, " ", transparency) buffer.text(x + 1, y + height, 0x000000, string.rep("▀", width), transparency) buffer.text(x + width, y, 0x000000, "▄", transparency) else buffer.square(x + width, y + 1, 2, height, 0x000000, 0x000000, " ", transparency) buffer.square(x + 2, y + height, width - 2, 1, 0x000000, 0x000000, " ", transparency) end 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 = tostring(text) text = (errorWindowParameters and errorWindowParameters.truncate) and unicode.sub(text, 1, errorWindowParameters.truncate) or text text = { text } end text = string.wrap(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"):draw() --Атрисовачка buffer.draw() end --Графонистый выход local function quit() OKButton:pressAndRelease(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 ----------------------------------------- Universal keyboard-input function ----------------------------------------- local function findValue(t, whatToSearch) if type(t) ~= "table" then return end for key, value in pairs(t) do if type(key) == "string" and string.match(key, "^" .. whatToSearch) then local valueType, postfix = type(value), "" if valueType == "function" or (valueType == "table" and getmetatable(value) and getmetatable(value).__call) then postfix = "()" elseif valueType == "table" then postfix = "." end return key .. postfix end end end local function findTable(whereToSearch, t, whatToSearch) local beforeFirstDot = string.match(whereToSearch, "^[^%.]+%.") -- Если вообще есть таблица, где надо искать if beforeFirstDot then beforeFirstDot = unicode.sub(beforeFirstDot, 1, -2) if t[beforeFirstDot] then return findTable(unicode.sub(whereToSearch, unicode.len(beforeFirstDot) + 2, -1), t[beforeFirstDot], whatToSearch) else -- Кароч, слушай суда: вот в эту зону хуйня может зайти толька -- тагда, кагда ты вручную ебенишь массив вида "abc.cda.blabla.test" -- без автозаполнения, т.е. он МОЖЕТ быть неверным, однако прога все -- равно проверяет на верность, и вот если НИ ХУЯ такого говнища типа -- ... .blabla не существует, то интерхпретатор захуяривается СУДЫ -- И ЧТОБ БОЛЬШЕ ВОПРОСОВ НЕ ЗАДАВАЛ!11! end -- Или если таблиц либо ваще нету, либо рекурсия суда вон вошла else return findValue(t[whereToSearch], whatToSearch) end end local function autocompleteVariables(sourceText) local varPath = string.match(sourceText, "[a-zA-Z0-9%.%_]+$") if varPath then local prefix = string.sub(sourceText, 1, -unicode.len(varPath) - 1) local whereToSearch = string.match(varPath, "[a-zA-Z0-9%.%_]+%.") if whereToSearch then whereToSearch = unicode.sub(whereToSearch, 1, -2) local findedTable = findTable(whereToSearch, _G, unicode.sub(varPath, unicode.len(whereToSearch) + 2, -1)) return findedTable and prefix .. whereToSearch .. "." .. findedTable or sourceText else local findedValue = findValue(_G, varPath) return findedValue and prefix .. findedValue or sourceText end else return sourceText end end -- local inputProperties = { -- --Метод, получающий на вход текущий текст и проверяющий вводимые данные. В случае успеха должен возвращать true -- validator = function(text) if string.match(text, "^%d+$") then return true end -- -- Автоматически очищает текстовое поле при начале ввода информации в него. -- -- Если при окончании ввода тексет не будет соответствовать методу validator, указанному выше, -- -- то будет возвращено исходное значение текста (не очищенное) -- eraseTextWhenInputBegins = true -- --Отключает символ многоточия при выходе за пределы текстового поля -- disableDots = true, -- --Попросту отрисовывает всю необходимую информацию без активации нажатия на клавиши -- justDrawNotEvent = true, -- --Задержка между миганимем курсора -- cursorBlinkDelay = 1.5, -- --Цвет курсора -- cursorColor = 0xFF7777, -- --Символ, используемый для отрисовки курсора -- cursorSymbol = "▌", -- --Символ-маскировщик, на который будет визуально заменен весь вводимый текст. Полезно для полей ввода пароля -- maskTextWithSymbol = "*", -- --Активация подсветки Lua-синтаксиса -- highlightLuaSyntax = true, -- -- Активация автозаполнения названий переменных по нажатию клавиши Tab -- autocompleteVariables = true, -- } function GUI.input(x, y, width, foreground, startText, inputProperties) inputProperties = inputProperties or {} if not (y >= buffer.drawLimit.y and y <= buffer.drawLimit.y2) then return startText end local text = startText if not inputProperties.justDrawNotEvent and inputProperties.eraseTextWhenInputBegins then text = "" end local textLength = unicode.len(text) local cursorBlinkState = false local cursorBlinkDelay = inputProperties.cursorBlinkDelay and inputProperties.cursorBlinkDelay or 0.5 local cursorColor = inputProperties.cursorColor and inputProperties.cursorColor or 0x00A8FF local cursorSymbol = inputProperties.cursorSymbol and inputProperties.cursorSymbol or "┃" local oldPixels = {}; for i = x, x + width - 1 do table.insert(oldPixels, { buffer.get(i, y) }) end local function drawOldPixels() for i = 1, #oldPixels do buffer.set(x + i - 1, y, oldPixels[i][1], oldPixels[i][2], oldPixels[i][3]) end end local function getTextLength() textLength = unicode.len(text) end local function textFormat() local formattedText = text if inputProperties.maskTextWithSymbol then formattedText = string.rep(inputProperties.maskTextWithSymbol or "*", textLength) end if textLength > width then if inputProperties.disableDots then formattedText = unicode.sub(formattedText, -width, -1) else formattedText = "…" .. unicode.sub(formattedText, -width + 1, -1) end end return formattedText end local function draw() drawOldPixels() if inputProperties.highlightLuaSyntax then require("syntax").highlightString(x, y, textFormat(), 1, width) else buffer.text(x, y, foreground, textFormat()) end if cursorBlinkState then local cursorPosition = textLength < width and x + textLength or x + width - 1 local bg = buffer.get(cursorPosition, y) buffer.set(cursorPosition, y, bg, cursorColor, cursorSymbol) end if not inputProperties.justDrawNotEvent then buffer.draw() end end local function backspace() if unicode.len(text) > 0 then text = unicode.sub(text, 1, -2); getTextLength(); draw() end end local function quit() cursorBlinkState = false if inputProperties.validator and not inputProperties.validator(text) then text = startText draw() return startText end draw() return text end draw() if inputProperties.justDrawNotEvent then return startText end while true do local e = { event.pull(cursorBlinkDelay) } if e[1] == "key_down" then if e[4] == 14 then backspace() elseif e[4] == 28 then return quit() elseif e[4] == 15 then if inputProperties.autocompleteVariables then text = autocompleteVariables(text); getTextLength(); draw() end else local symbol = unicode.char(e[3]) if not keyboard.isControl(e[3]) then text = text .. symbol getTextLength() draw() end end elseif e[1] == "clipboard" then text = text .. e[3] getTextLength() draw() elseif e[1] == "touch" then return quit() else cursorBlinkState = not cursorBlinkState draw() end end end ----------------------------------------- Input Text Box object ----------------------------------------- local function drawInputTextBox(object, isFocused) local background = isFocused and object.colors.focused.background or object.colors.default.background local foreground = 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, highlightLuaSyntax = isFocused and object.highlightLuaSyntax or nil, autocompleteVariables = object.autocompleteVariables or nil, maskTextWithSymbol = object.textMask or nil, validator = object.validator or nil, eraseTextWhenInputBegins = object.eraseTextOnFocus or nil, }) object.text = isFocused and resultText or object.text end local function inputTextBoxBeginInput(object) drawInputTextBox(object, true) if object.text == "" then object.text = nil; drawInputTextBox(object, false); buffer.draw() end end function GUI.inputTextBox(x, y, width, height, inputTextBoxColor, textColor, inputTextBoxFocusedColor, textFocusedColor, text, placeholderText, maskTextWithSymbol, eraseTextOnFocus) local object = GUI.object(x, y, width, height) object.colors = { default = { background = inputTextBoxColor, text = textColor }, focused = { background = inputTextBoxFocusedColor, text = textFocusedColor } } object.text = text object.placeholderText = placeholderText object.isClicked = isObjectClicked object.draw = drawInputTextBox object.input = inputTextBoxBeginInput object.eraseTextOnFocus = eraseTextOnFocus object.textMask = maskTextWithSymbol return object end ----------------------------------------- Text Box object ----------------------------------------- local function drawTextBox(object) if object.colors.background then buffer.square(object.x, object.y, object.width, object.height, object.colors.background, object.colors.text, " ") end local xPos, yPos = GUI.getAlignmentCoordinates(object, {width = 1, height = object.height - object.offset.vertical * 2}) local lineLimit = object.width - object.offset.horizontal * 2 for line = object.currentLine, object.currentLine + object.height - 1 do if object.lines[line] then local lineType, text, textColor = type(object.lines[line]) if lineType == "table" then text, textColor = string.limit(object.lines[line].text, lineLimit), object.lines[line].color elseif lineType == "string" then text, textColor = string.limit(object.lines[line], lineLimit), object.colors.text else error("Unknown TextBox line type: " .. tostring(lineType)) end xPos = GUI.getAlignmentCoordinates( { x = object.x + object.offset.horizontal, y = object.y + object.offset.vertical, width = object.width - object.offset.horizontal * 2, height = object.height - object.offset.vertical * 2, alignment = object.alignment }, {width = unicode.len(text), height = object.height} ) buffer.text(xPos, yPos, textColor, text) yPos = yPos + 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 = #lines return object end function GUI.textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset) local object = GUI.object(x, y, width, height) 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 = drawTextBox object.scrollUp = scrollUpTextBox object.scrollDown = scrollDownTextBox object.scrollToStart = scrollToStartTextBox object.scrollToEnd = scrollToEndTextBox object.offset = {horizontal = horizontalOffset or 0, vertical = verticalOffset or 0} return object end ----------------------------------------- Horizontal Slider Object ----------------------------------------- local function drawHorizontalSlider(object) -- На всякий случай делаем значение не меньше минимального и не больше максимального object.value = math.min(math.max(object.value, object.minimumValue), object.maximumValue) -- Отображаем максимальное и минимальное значение, если требуется if object.showMaximumAndMinimumValues then local stringMaximumValue, stringMinimumValue = tostring(object.roundValues and math.floor(object.maximumValue) or math.roundToDecimalPlaces(object.maximumValue, 2)), tostring(object.roundValues and math.floor(object.maximumValue) or math.roundToDecimalPlaces(object.minimumValue, 2)) buffer.text(object.x - unicode.len(stringMinimumValue) - 1, object.y, object.colors.value, stringMinimumValue) buffer.text(object.x + object.width + 1, object.y, object.colors.value, stringMaximumValue) end -- А еще текущее значение рисуем, если хочется нам if object.currentValuePrefix or object.currentValuePostfix then local stringCurrentValue = (object.currentValuePrefix or "") .. (object.roundValues and math.floor(object.value) or math.roundToDecimalPlaces(object.value, 2)) .. (object.currentValuePostfix or "") buffer.text(math.floor(object.x + object.width / 2 - unicode.len(stringCurrentValue) / 2), object.y + 1, object.colors.value, stringCurrentValue) end -- Рисуем сам слайдер local activeWidth = math.floor(object.width - ((object.maximumValue - object.value) * object.width / (object.maximumValue - object.minimumValue))) buffer.text(object.x, object.y, object.colors.passive, string.rep("━", object.width)) buffer.text(object.x, object.y, object.colors.active, string.rep("━", activeWidth)) buffer.square(object.x + activeWidth - 1, object.y, 2, 1, object.colors.pipe, 0x000000, " ") return object end function GUI.horizontalSlider(x, y, width, activeColor, passiveColor, pipeColor, valueColor, minimumValue, maximumValue, value, showMaximumAndMinimumValues, currentValuePrefix, currentValuePostfix) local object = GUI.object(x, y, width, 1) object.colors = {active = activeColor, passive = passiveColor, pipe = pipeColor, value = valueColor} object.draw = drawHorizontalSlider object.minimumValue = minimumValue object.maximumValue = maximumValue object.value = value object.showMaximumAndMinimumValues = showMaximumAndMinimumValues object.currentValuePrefix = currentValuePrefix object.currentValuePostfix = currentValuePostfix object.roundValues = false return object end ----------------------------------------- Switch object ----------------------------------------- local function drawSwitch(object) local pipeWidth = object.height * 2 local pipePosition, backgroundColor if object.state then pipePosition, backgroundColor = object.x + object.width - pipeWidth, object.colors.active else pipePosition, backgroundColor = object.x, object.colors.passive end buffer.square(object.x, object.y, object.width, object.height, backgroundColor, 0x000000, " ") buffer.square(pipePosition, object.y, pipeWidth, object.height, object.colors.pipe, 0x000000, " ") return object end function GUI.switch(x, y, width, activeColor, passiveColor, pipeColor, state) local object = GUI.object(x, y, width, 1) object.colors = {active = activeColor, passive = passiveColor, pipe = pipeColor, value = valueColor} object.draw = drawSwitch object.state = state or false return object end ----------------------------------------- Chart object ----------------------------------------- local function drawChart(object) -- Ебошем пездатые оси for i = object.y, object.y + object.height - 2 do buffer.text(object.x, i, object.colors.axis, "│") end buffer.text(object.x + 1, object.y + object.height - 1, object.colors.axis, string.rep("─", object.width - 1)) buffer.text(object.x, object.y + object.height - 1, object.colors.axis, "└") if #object.values > 1 then local oldDrawLimit = buffer.getDrawLimit() buffer.setDrawLimit(object.x, object.y, object.width, object.height) local delta, fieldWidth, fieldHeight = object.maximumValue - object.minimumValue, object.width - 2, object.height - 1 -- Рисуем линии значений local roundValues = object.maximumValue > 10 local step = 0.2 * fieldHeight for i = step, fieldHeight, step do local value = object.minimumValue + delta * (i / fieldHeight) local stringValue = roundValues and tostring(math.floor(value)) or math.doubleToString(value, 1) buffer.text(object.x + 1, math.floor(object.y + fieldHeight - i), object.colors.value, string.rep("─", object.width - unicode.len(stringValue) - 2) .. " " .. stringValue) end -- Рисуем графек, йопта local function getDotPosition(valueIndex) return object.x + math.round((fieldWidth * (valueIndex - 1) / (#object.values - 1))) + 1, object.y + math.round(((fieldHeight - 1) * (object.maximumValue - object.values[valueIndex]) / delta)) end local x, y = getDotPosition(1) for valueIndex = 2, #object.values do local xNew, yNew = getDotPosition(valueIndex) buffer.semiPixelLine(x, y * 2, xNew, yNew * 2, object.colors.chart) x, y = xNew, yNew end buffer.setDrawLimit(oldDrawLimit) end -- Дорисовываем названия осей if object.axisNames.y then buffer.text(object.x + 1, object.y, object.colors.axis, object.axisNames.y) end if object.axisNames.x then buffer.text(object.x + object.width - unicode.len(object.axisNames.x), object.y + object.height - 2, object.colors.axis, object.axisNames.x) end end function GUI.chart(x, y, width, height, axisColor, axisValueColor, chartColor, xAxisName, yAxisName, minimumValue, maximumValue, values) if minimumValue >= maximumValue then error("Chart's minimum value can't be >= maximum value!") end local object = GUI.object(x, y, width, height) object.colors = {axis = axisColor, chart = chartColor, value = axisValueColor} object.draw = drawChart object.values = values object.minimumValue = minimumValue object.maximumValue = maximumValue object.axisNames = {x = xAxisName, y = yAxisName} return object end ----------------------------------------- Combo Box Object ----------------------------------------- local function drawComboBox(object) buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background) local x, y, limit, arrowSize = object.x + 1, math.floor(object.y + object.height / 2), object.width - 5, object.height buffer.text(x, y, object.colors.default.text, string.limit(object.items[object.currentItem].text, limit, false)) GUI.button(object.x + object.width - arrowSize * 2 + 1, object.y, arrowSize * 2 - 1, arrowSize, object.colors.arrow.background, object.colors.arrow.text, 0x0, 0x0, object.state and "▲" or "▼"):draw() end local function selectComboBoxItem(object) object.state = true object:draw() local dropDownMenu = GUI.dropDownMenu(object.x, object.y + object.height, object.width, object.height == 1 and 0 or 1, object.colors.default.background, object.colors.default.text, object.colors.pressed.background, object.colors.pressed.text, GUI.colors.contextMenu.disabled.text, GUI.colors.contextMenu.separator, GUI.colors.contextMenu.transparency.background, object.items) dropDownMenu.items = object.items dropDownMenu.sidesOffset = 1 local _, itemIndex = dropDownMenu:show() object.currentItem = itemIndex or object.currentItem object.state = false object:draw() buffer.draw() end function GUI.comboBox(x, y, width, height, backgroundColor, textColor, arrowBackgroundColor, arrowTextColor, items) local object = GUI.object(x, y, width, height) object.colors = { default = { background = backgroundColor, text = textColor }, pressed = { background = GUI.colors.contextMenu.pressed.background, text = GUI.colors.contextMenu.pressed.text }, arrow = { background = arrowBackgroundColor, text = arrowTextColor } } object.items = {} object.currentItem = 1 object.addItem = addDropDownMenuItem object.addSeparator = addDropDownMenuSeparator if items then for i = 1, #items do object:addItem(items[i]) end end object.draw = drawComboBox object.selectItem = selectComboBoxItem object.state = false return object end -------------------------------------------------------------------------------------------------------------------------------- -- buffer.start() -- buffer.clear(0x1b1b1b) -- local comboBox = GUI.comboBox(2, 2, 30, 1, 0xFFFFFF, 0x262626, 0xDDDDDD, 0x262626, {"PIC", "RAW", "PNG", "JPG"}) -- comboBox:selectItem() -- buffer.draw() -- GUI.chart(2, 10, 40, 20, 0xFFFFFF, 0xBBBBBB, 0xFFDB40, "t", "EU", 0, 2, { -- 0.5, -- 0.12 -- }):draw() -- local menu = GUI.dropDownMenu(2, 2, 40, 1, 0xFFFFFF, 0x000000, 0xFFDB40, 0xFFFFFF, 0x999999, 0x777777, 50) -- menu:addItem("New") -- menu:addItem("Open") -- menu:addSeparator() -- menu:addItem("Save") -- menu:addItem("Save as") -- menu:show() -- GUI.contextMenu(2, 2, {"Hello"}, {"World"}, "-", {"You are the"}, {"Best of best", false, "^S"}, {"And bestest yopta"}):show() -------------------------------------------------------------------------------------------------------------------------------- return GUI