MineOS/lib/GUI.lua
Igor Timofeev c652230924 aef
2017-01-12 14:18:29 +03:00

1521 lines
57 KiB
Lua
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

----------------------------------------- Libraries -----------------------------------------
require("advancedLua")
local keyboard = require("keyboard")
local buffer = require("doubleBuffering")
local unicode = require("unicode")
local event = require("event")
----------------------------------------- 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(
"unknown",
"empty",
"panel",
"label",
"button",
"framedButton",
"image",
"windowActionButtons",
"windowActionButton",
"tabBar",
"tabBarTab",
"menu",
"menuItem",
"window",
"inputTextBox",
"textBox",
"horizontalSlider",
"switch",
"progressBar",
"chart",
"comboBox",
"scrollBar"
)
----------------------------------------- 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.isHidden 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
function GUI.point(x, y)
return { x = x, y = y }
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 -----------------------------------------
-- 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].isHidden 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 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
local function containerGetFirstParent(object)
if object.parent then
local currentParent = object.parent
while currentParent.parent do
currentParent = currentParent.parent
end
return currentParent
else
error("Object doesn't have any parents")
end
end
-- Add any object as children to parent container with specified objectType
function GUI.addChildToContainer(container, object, objectType)
object.type = objectType or GUI.objectTypes.unknown
object.parent = container
object.indexOf = containerObjectIndexOf
object.moveToFront = containerObjectMoveToFront
object.moveToBack = containerObjectMoveToBack
object.moveForward = containerObjectMoveForward
object.moveBackward = containerObjectMoveBackward
object.getFirstParent = containerGetFirstParent
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, ...)
return GUI.addChildToContainer(container, GUI.object(...), GUI.objectTypes.empty)
end
-- Add button object to container
local function addButtonObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.button(...), GUI.objectTypes.button)
end
-- Add adaptive button object to container
local function addAdaptiveButtonObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.adaptiveButton(...), GUI.objectTypes.button)
end
-- Add framedButton object to container
local function addFramedButtonObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.framedButton(...), GUI.objectTypes.button)
end
-- Add adaptive framedButton object to container
local function addAdaptiveFramedButtonObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.adaptiveFramedButton(...), GUI.objectTypes.button)
end
-- Add label object to container
local function addLabelObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.label(...), GUI.objectTypes.label)
end
-- Add panel object to container
local function addPanelObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.panel(...), GUI.objectTypes.panel)
end
-- Add windowActionButtons object to container
local function addWindowActionButtonsObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.windowActionButtons(...), GUI.objectTypes.windowActionButtons)
end
-- Add another container to container
local function addContainerToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.container(...), GUI.objectTypes.container)
end
-- Add image object to container
local function addImageObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.image(...), GUI.objectTypes.image)
end
-- Add image object to container
local function addTabBarObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.tabBar(...), GUI.objectTypes.tabBar)
end
-- Add InputTextBox object to container
local function addInputTextBoxObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.inputTextBox(...), GUI.objectTypes.inputTextBox)
end
-- Add TextBox object to container
local function addTextBoxObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.textBox(...), GUI.objectTypes.textBox)
end
-- Add Horizontal Slider object to container
local function addHorizontalSliderObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.horizontalSlider(...), GUI.objectTypes.horizontalSlider)
end
-- Add Progressbar object to container
local function addProgressBarObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.progressBar(...), GUI.objectTypes.progressBar)
end
-- Add Switch object to container
local function addSwitchObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.switch(...), GUI.objectTypes.switch)
end
-- Add Chart object to container
local function addChartObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.chart(...), GUI.objectTypes.chart)
end
-- Add ComboBox object to container
local function addComboBoxObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.comboBox(...), GUI.objectTypes.comboBox)
end
-- Add Menu object to container
local function addMenuObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.menu(...), GUI.objectTypes.menu)
end
-- Add Menu object to container
local function addScrollBarObjectToContainer(container, ...)
return GUI.addChildToContainer(container, GUI.scrollBar(...), GUI.objectTypes.scrollBar)
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].isHidden 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
-- cyka blyad
-- 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 .. " 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 = {}
container.draw = drawContainerContent
container.getClickedObject = GUI.getClickedObject
container.deleteChildren = deleteContainersContent
container.addChild = GUI.addChildToContainer
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
container.addMenu = addMenuObjectToContainer
container.addScrollBar = addScrollBarObjectToContainer
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(1, 1, object.width, object.height, backgroundColor)
object.tabs = object:addContainer(1, 1, object.width, object.height)
x = math.floor(width / 2 - object.tabsWidth / 2)
for elementIndex = 1, #elements do
local tab = object.tabs:addButton(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 -----------------------------------------
function GUI.windowActionButtons(x, y, fatSymbol)
local symbol = fatSymbol and "" or ""
local container = GUI.container(x, y, 5, 1)
container.close = container:addButton(1, 1, 1, 1, nil, 0xFF4940, nil, 0x992400, symbol)
container.minimize = container:addButton(3, 1, 1, 1, nil, 0xFFB640, nil, 0x996D00, symbol)
container.maximize = container:addButton(5, 1, 1, 1, nil, 0x00B640, nil, 0x006D40, symbol)
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()
if object.items[itemIndex].onTouch then object.items[itemIndex].onTouch() end
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
return 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 -----------------------------------------
local function menuDraw(menu)
buffer.square(menu.x, menu.y, menu.width, 1, menu.colors.default.background, menu.colors.default.text, " ", menu.colors.transparency)
menu:reimplementedDraw()
end
local function menuAddItem(menu, text, textColor)
local x = 2; for i = 1, #menu.children do x = x + unicode.len(menu.children[i].text) + 2; end
local item = menu:addAdaptiveButton(x, 1, 1, 0, nil, textColor or menu.colors.default.text, menu.colors.pressed.background, menu.colors.pressed.text, text)
item.type = GUI.objectTypes.menuItem
return item
end
function GUI.menu(x, y, width, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundTransparency, ...)
local menu = GUI.container(x, y, width, 1)
menu.colors = {
default = {
background = backgroundColor,
text = textColor,
},
pressed = {
background = backgroundPressedColor,
text = textPressedColor,
},
transparency = backgroundTransparency
}
menu.addItem = menuAddItem
menu.reimplementedDraw = menu.draw
menu.draw = menuDraw
return menu
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 function inputFieldDraw(inputField)
if inputField.x < 1 or inputField.y < 1 or inputField.x + inputField.width - 1 > buffer.screen.width or inputField.y > buffer.screen.height then return inputField end
if inputField.oldPixels then
buffer.paste(inputField.x, inputField.y, inputField.oldPixels)
else
inputField.oldPixels = buffer.copy(inputField.x, inputField.y, inputField.width, 1)
end
if inputField.highlightLuaSyntax then
require("syntax").highlightString(inputField.x, inputField.y, inputField.text, inputField.textCutFrom, inputField.width)
else
buffer.text(
inputField.x,
inputField.y,
inputField.colors.text,
unicode.sub(
inputField.textMask and string.rep(inputField.textMask, unicode.len(inputField.text)) or inputField.text,
inputField.textCutFrom,
inputField.textCutFrom + inputField.width - 1
)
)
end
if inputField.cursorBlinkState then
buffer.text(inputField.x + inputField.cursorPosition - inputField.textCutFrom, inputField.y, inputField.cursorColor, inputField.cursorSymbol)
end
return inputField
end
local function inputFieldSetCursorPosition(inputField, newPosition)
if newPosition < 1 then
newPosition = 1
elseif newPosition > unicode.len(inputField.text) + 1 then
newPosition = unicode.len(inputField.text) + 1
end
if newPosition > inputField.textCutFrom + inputField.width - 1 then
inputField.textCutFrom = inputField.textCutFrom + newPosition - (inputField.textCutFrom + inputField.width - 1)
elseif newPosition < inputField.textCutFrom then
inputField.textCutFrom = newPosition
end
inputField.cursorPosition = newPosition
return inputField
end
local function inputFieldBeginInput(inputField)
inputField.cursorBlinkState = true; inputField:draw(); buffer.draw()
while true do
local e = { event.pull(inputField.cursorBlinkDelay) }
if e[1] == "touch" or e[1] == "drag" then
if inputField:isClicked(e[3], e[4]) then
inputField:setCursorPosition(inputField.textCutFrom + e[3] - inputField.x)
inputField.cursorBlinkState = true; inputField:draw(); buffer.draw()
else
inputField.cursorBlinkState = false; inputField:draw(); buffer.draw()
return inputField
end
elseif e[1] == "key_down" then
if e[4] == 28 then
inputField.cursorBlinkState = false; inputField:draw(); buffer.draw()
return inputField
elseif e[4] == 15 then
if inputField.autocompleteVariables then
inputField.text = autocompleteVariables(inputField.text)
inputField:setCursorPosition(unicode.len(inputField.text) + 1)
inputField.cursorBlinkState = true; inputField:draw(); buffer.draw()
end
elseif e[4] == 203 then
inputField:setCursorPosition(inputField.cursorPosition - 1)
inputField.cursorBlinkState = true; inputField:draw(); buffer.draw()
elseif e[4] == 205 then
inputField:setCursorPosition(inputField.cursorPosition + 1)
inputField.cursorBlinkState = true; inputField:draw(); buffer.draw()
elseif e[4] == 14 then
inputField.text = unicode.sub(unicode.sub(inputField.text, 1, inputField.cursorPosition - 1), 1, -2) .. unicode.sub(inputField.text, inputField.cursorPosition, -1)
inputField:setCursorPosition(inputField.cursorPosition - 1)
inputField.cursorBlinkState = true; inputField:draw(); buffer.draw()
else
if not keyboard.isControl(e[3]) then
inputField.text = unicode.sub(inputField.text, 1, inputField.cursorPosition - 1) .. unicode.char(e[3]) .. unicode.sub(inputField.text, inputField.cursorPosition, -1)
inputField:setCursorPosition(inputField.cursorPosition + 1)
inputField.cursorBlinkState = true; inputField:draw(); buffer.draw()
end
end
elseif e[1] == "clipboard" then
inputField.text = unicode.sub(inputField.text, 1, inputField.cursorPosition - 1) .. e[3] .. unicode.sub(inputField.text, inputField.cursorPosition, -1)
inputField:setCursorPosition(inputField.cursorPosition + unicode.len(e[3]))
inputField.cursorBlinkState = true; inputField:draw(); buffer.draw()
else
inputField.cursorBlinkState = not inputField.cursorBlinkState; inputField:draw(); buffer.draw()
end
end
end
function GUI.inputField(x, y, width, textColor, text, textMask, highlightLuaSyntax, autocompleteVariables)
local inputField = GUI.object(x, y, width, 1)
inputField.textCutFrom = 1
inputField.cursorPosition = 1
inputField.cursorColor = 0x00A8FF
inputField.cursorSymbol = ""
inputField.cursorBlinkDelay = 0.4
inputField.cursorBlinkState = false
inputField.colors = {text = textColor}
inputField.text = text
inputField.textMask = textMask
inputField.highlightLuaSyntax = highlightLuaSyntax
inputField.autocompleteVariables = autocompleteVariables
inputField.setCursorPosition = inputFieldSetCursorPosition
inputField.draw = inputFieldDraw
inputField.input = inputFieldBeginInput
inputField:setCursorPosition(unicode.len(inputField.text) + 1)
return inputField
end
----------------------------------------- Input Text Box object -----------------------------------------
local function drawInputTextBox(inputTextBox)
local background = inputTextBox.isFocused and inputTextBox.colors.focused.background or inputTextBox.colors.default.background
local foreground = inputTextBox.isFocused and inputTextBox.colors.focused.text or inputTextBox.colors.default.text
local y = math.floor(inputTextBox.y + inputTextBox.height / 2)
local text = inputTextBox.isFocused and (inputTextBox.eraseTextOnFocus and "" or inputTextBox.text) or (inputTextBox.text ~= "" and inputTextBox.text or inputTextBox.placeholderText or "")
if background then
buffer.square(inputTextBox.x, inputTextBox.y, inputTextBox.width, inputTextBox.height, background, foreground, " ")
end
local inputField = GUI.inputField(inputTextBox.x + 1, y, inputTextBox.width - 2, foreground, text, inputTextBox.textMask, inputTextBox.highlightLuaSyntax, inputTextBox.autocompleteVariables)
if inputTextBox.isFocused then
inputField:input()
if inputTextBox.validator then
if inputTextBox.validator(inputField.text) then
inputTextBox.text = inputField.text
end
else
inputTextBox.text = inputField.text
end
else
local oldHighlightLuaSyntaxValue = inputField.highlightLuaSyntax
inputField.highlightLuaSyntax = false
inputField:draw()
inputField.highlightLuaSyntax = oldHighlightLuaSyntaxValue
end
return inputTextBox
end
local function inputTextBoxBeginInput(inputTextBox)
inputTextBox.isFocused = true
inputTextBox:draw()
inputTextBox.isFocused = false
return inputTextBox
end
function GUI.inputTextBox(x, y, width, height, inputTextBoxColor, textColor, inputTextBoxFocusedColor, textFocusedColor, text, placeholderText, eraseTextOnFocus, textMask, highlightLuaSyntax, autocompleteVariables)
local inputTextBox = GUI.object(x, y, width, height)
inputTextBox.colors = {
default = {
background = inputTextBoxColor,
text = textColor
},
focused = {
background = inputTextBoxFocusedColor,
text = textFocusedColor
}
}
inputTextBox.text = text
inputTextBox.placeholderText = placeholderText
inputTextBox.draw = drawInputTextBox
inputTextBox.input = inputTextBoxBeginInput
inputTextBox.eraseTextOnFocus = eraseTextOnFocus
inputTextBox.textMask = textMask
return inputTextBox
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
----------------------------------------- Scrollbar object -----------------------------------------
local function scrollBarDraw(scrollBar)
local isVertical = scrollBar.height > scrollBar.width
local valuesDelta = scrollBar.maximumValue - scrollBar.minimumValue + 1
local part = scrollBar.value / valuesDelta
local barSize = math.ceil(scrollBar.shownValueCount / valuesDelta * scrollBar.height)
local halfBarSize = math.floor(barSize / 2)
buffer.square(scrollBar.x, scrollBar.y, scrollBar.width, scrollBar.height, scrollBar.colors.background, 0x0, " ")
if isVertical then
scrollBar.ghostPosition.x = scrollBar.x
scrollBar.ghostPosition.y = scrollBar.y + halfBarSize
scrollBar.ghostPosition.width = scrollBar.width
scrollBar.ghostPosition.height = scrollBar.height - barSize
buffer.square(
scrollBar.ghostPosition.x,
math.floor(scrollBar.ghostPosition.y + part * scrollBar.ghostPosition.height - halfBarSize),
scrollBar.ghostPosition.width,
barSize,
scrollBar.colors.foreground, 0x0, " "
)
else
scrollBar.ghostPosition.x = scrollBar.x + halfBarSize
scrollBar.ghostPosition.y = scrollBar.y
scrollBar.ghostPosition.width = scrollBar.width - barSize
scrollBar.ghostPosition.height = scrollBar.height
buffer.square(
math.floor(scrollBar.ghostPosition.x + part * scrollBar.ghostPosition.width - halfBarSize),
scrollBar.ghostPosition.y,
barSize,
scrollBar.ghostPosition.height,
scrollBar.colors.foreground, 0x0, " "
)
end
return scrollBar
end
function GUI.scrollBar(x, y, width, height, backgroundColor, foregroundColor, minimumValue, maximumValue, value, shownValueCount, onScrollValueIncrement, horizontalThin)
local scrollBar = GUI.object(x, y, width, height)
scrollBar.maximumValue = maximumValue
scrollBar.minimumValue = minimumValue
scrollBar.value = value
scrollBar.onScrollValueIncrement = onScrollValueIncrement
scrollBar.shownValueCount = shownValueCount
scrollBar.thin = horizontalThin
scrollBar.colors = {
background = backgroundColor,
foreground = foregroundColor,
}
scrollBar.ghostPosition = {}
scrollBar.draw = scrollBarDraw
return scrollBar
end
--------------------------------------------------------------------------------------------------------------------------------
buffer.start()
buffer.clear(0x1b1b1b)
-- GUI.scrollBar(1, 5, 1, 20, 0x444444, 0x00DBFF, 1, 100, 50, 20, 1, true):draw()
-- buffer.draw()
-- 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