Advertisement
Guest User

GUI.lua

a guest
Feb 26th, 2017
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 54.23 KB | None | 0 0
  1.  
  2. ----------------------------------------- Libraries -----------------------------------------
  3.  
  4. local libraries = {
  5.     advancedLua = "advancedLua",
  6.     buffer = "doubleBuffering",
  7.     unicode = "unicode",
  8.     event = "event",
  9. }
  10.  
  11. for library in pairs(libraries) do if not _G[library] then _G[library] = require(libraries[library]) end end; libraries = nil
  12.  
  13. ----------------------------------------- Core constants -----------------------------------------
  14.  
  15. local GUI = {}
  16.  
  17. GUI.alignment = {
  18.     horizontal = enum(
  19.         "left",
  20.         "center",
  21.         "right"
  22.     ),
  23.     vertical = enum(
  24.         "top",
  25.         "center",
  26.         "bottom"
  27.     )
  28. }
  29.  
  30. GUI.directions = enum(
  31.     "horizontal",
  32.     "vertical"
  33. )
  34.  
  35. GUI.colors = {
  36.     disabled = {
  37.         background = 0x888888,
  38.         text = 0xAAAAAA
  39.     },
  40.     contextMenu = {
  41.         separator = 0xAAAAAA,
  42.         default = {
  43.             background = 0xFFFFFF,
  44.             text = 0x2D2D2D
  45.         },
  46.         disabled = {
  47.             text = 0xAAAAAA
  48.         },
  49.         pressed = {
  50.             background = 0x3366CC,
  51.             text = 0xFFFFFF
  52.         },
  53.         transparency = {
  54.             background = 20,
  55.             shadow = 50
  56.         }
  57.     }
  58. }
  59.  
  60. GUI.dropDownMenuElementTypes = enum(
  61.     "default",
  62.     "separator"
  63. )
  64.  
  65. GUI.objectTypes = enum(
  66.     "unknown",
  67.     "empty",
  68.     "panel",
  69.     "label",
  70.     "button",
  71.     "framedButton",
  72.     "image",
  73.     "windowActionButtons",
  74.     "windowActionButton",
  75.     "tabBar",
  76.     "tabBarTab",
  77.     "menu",
  78.     "menuElement",
  79.     "window",
  80.     "inputTextBox",
  81.     "textBox",
  82.     "horizontalSlider",
  83.     "switch",
  84.     "progressBar",
  85.     "chart",
  86.     "comboBox"
  87. )
  88.  
  89. ----------------------------------------- Primitive objects -----------------------------------------
  90.  
  91. -- Universal method to check if object was clicked by following coordinates
  92. local function isObjectClicked(object, x, y)
  93.     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
  94.     return false
  95. end
  96.  
  97. -- Limit object's text field to its' size
  98. local function objectTextLimit(object)
  99.     local text, textLength = object.text, unicode.len(object.text)
  100.     if textLength > object.width then text = unicode.sub(text, 1, object.width); textLength = object.width end
  101.     return text, textLength
  102. end
  103.  
  104. -- Base object to use in everything
  105. function GUI.object(x, y, width, height)
  106.     return {
  107.         x = x,
  108.         y = y,
  109.         width = width,
  110.         height = height,
  111.         isClicked = isObjectClicked
  112.     }
  113. end
  114.  
  115. ----------------------------------------- Object alignment -----------------------------------------
  116.  
  117. -- Set children alignment in parent object
  118. function GUI.setAlignment(object, horizontalAlignment, verticalAlignment)
  119.     object.alignment = {
  120.         horizontal = horizontalAlignment,
  121.         vertical = verticalAlignment
  122.     }
  123.     return object
  124. end
  125.  
  126. -- Get subObject position inside of parent object
  127. function GUI.getAlignmentCoordinates(object, subObject)
  128.     local x, y
  129.     if object.alignment.horizontal == GUI.alignment.horizontal.left then
  130.         x = object.x
  131.     elseif object.alignment.horizontal == GUI.alignment.horizontal.center then
  132.         x = math.floor(object.x + object.width / 2 - subObject.width / 2)
  133.     elseif object.alignment.horizontal == GUI.alignment.horizontal.right then
  134.         x = object.x + object.width - subObject.width
  135.     else
  136.         error("Unknown horizontal alignment: " .. tostring(object.alignment.horizontal))
  137.     end
  138.  
  139.     if object.alignment.vertical == GUI.alignment.vertical.top then
  140.         y = object.y
  141.     elseif object.alignment.vertical == GUI.alignment.vertical.center then
  142.         y = math.floor(object.y + object.height / 2 - subObject.height / 2)
  143.     elseif object.alignment.vertical == GUI.alignment.vertical.bottom then
  144.         y = object.y + object.height - subObject.height
  145.     else
  146.         error("Unknown vertical alignment: " .. tostring(object.alignment.vertical))
  147.     end
  148.  
  149.     return x, y
  150. end
  151.  
  152. ----------------------------------------- Containers -----------------------------------------
  153.  
  154. -- Go recursively through every container's object (including other containers) and return object that was clicked firstly by it's GUI-layer position
  155. function GUI.getClickedObject(container, xEvent, yEvent)
  156.     local clickedObject, clickedIndex
  157.     for objectIndex = #container.children, 1, -1 do
  158.         if not container.children[objectIndex].hidden then
  159.             container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1
  160.             if container.children[objectIndex].children and #container.children[objectIndex].children > 0 then
  161.                 clickedObject, clickedIndex = GUI.getClickedObject(container.children[objectIndex], xEvent, yEvent)
  162.                 if clickedObject then break end
  163.             elseif not container.children[objectIndex].disableClicking and container.children[objectIndex]:isClicked(xEvent, yEvent) then
  164.                 clickedObject, clickedIndex = container.children[objectIndex], objectIndex
  165.                 break
  166.             end
  167.         end
  168.     end
  169.  
  170.     return clickedObject, clickedIndex
  171. end
  172.  
  173. local function checkObjectParentExists(object)
  174.     if not object.parent then error("Object doesn't have a parent container") end
  175. end
  176.  
  177. local function containerObjectIndexOf(object)
  178.     checkObjectParentExists(object)
  179.     for objectIndex = 1, #object.parent.children do
  180.         if object.parent.children[objectIndex] == object then
  181.             return objectIndex
  182.         end
  183.     end
  184. end
  185.  
  186. -- Move container's object "closer" to our eyes
  187. local function containerObjectMoveForward(object)
  188.     local objectIndex = object:indexOf()
  189.     if objectIndex < #object.parent.children then
  190.         object.parent.children[index], object.parent.children[index + 1] = swap(object.parent.children[index], object.parent.children[index + 1])
  191.     end
  192. end
  193.  
  194. -- Move container's object "more far out" of our eyes
  195. local function containerObjectMoveBackward(object)
  196.     local objectIndex = object:indexOf()
  197.     if objectIndex > 1 then
  198.         object.parent.children[index], object.parent.children[index - 1] = swap(object.parent.children[index], object.parent.children[index - 1])
  199.     end
  200. end
  201.  
  202. -- Move container's object to front of all objects
  203. local function containerObjectMoveToFront(object)
  204.     local objectIndex = object:indexOf()
  205.     table.insert(object.parent.children, object)
  206.     table.remove(object.parent.children, objectIndex)
  207. end
  208.  
  209. -- Move container's object to back of all objects
  210. local function containerObjectMoveToBack(object)
  211.     local objectIndex = object:indexOf()
  212.     table.insert(object.parent.children, 1, object)
  213.     table.remove(object.parent.children, objectIndex + 1)
  214. end
  215.  
  216. -- Add any object as children to parent container with specified objectType
  217. function GUI.addChildToContainer(container, object, objectType)
  218.     object.type = objectType or GUI.objectTypes.unknown
  219.     object.parent = container
  220.     object.indexOf = containerObjectIndexOf
  221.     object.moveToFront = containerObjectMoveToFront
  222.     object.moveToBack = containerObjectMoveToBack
  223.     object.moveForward = containerObjectMoveForward
  224.     object.moveBackward = containerObjectMoveBackward
  225.     object.localPosition = {x = object.x, y = object.y}
  226.  
  227.     table.insert(container.children, object)
  228.    
  229.     return object
  230. end
  231.  
  232. -- Add empty GUI.object to container
  233. local function addEmptyObjectToContainer(container, ...)
  234.     return GUI.addChildToContainer(container, GUI.object(...), GUI.objectTypes.empty)
  235. end
  236.  
  237. -- Add button object to container
  238. local function addButtonObjectToContainer(container, ...)
  239.     return GUI.addChildToContainer(container, GUI.button(...), GUI.objectTypes.button)
  240. end
  241.  
  242. -- Add adaptive button object to container
  243. local function addAdaptiveButtonObjectToContainer(container, ...)
  244.     return GUI.addChildToContainer(container, GUI.adaptiveButton(...), GUI.objectTypes.button)
  245. end
  246.  
  247. -- Add framedButton object to container
  248. local function addFramedButtonObjectToContainer(container, ...)
  249.     return GUI.addChildToContainer(container, GUI.framedButton(...), GUI.objectTypes.button)
  250. end
  251.  
  252. -- Add adaptive framedButton object to container
  253. local function addAdaptiveFramedButtonObjectToContainer(container, ...)
  254.     return GUI.addChildToContainer(container, GUI.adaptiveFramedButton(...), GUI.objectTypes.button)
  255. end
  256.  
  257. -- Add label object to container
  258. local function addLabelObjectToContainer(container, ...)
  259.     return GUI.addChildToContainer(container, GUI.label(...), GUI.objectTypes.label)
  260. end
  261.  
  262. -- Add panel object to container
  263. local function addPanelObjectToContainer(container, ...)
  264.     return GUI.addChildToContainer(container, GUI.panel(...), GUI.objectTypes.panel)
  265. end
  266.  
  267. -- Add windowActionButtons object to container
  268. local function addWindowActionButtonsObjectToContainer(container, ...)
  269.     return GUI.addChildToContainer(container, GUI.windowActionButtons(...), GUI.objectTypes.windowActionButtons)
  270. end
  271.  
  272. -- Add another container to container
  273. local function addContainerToContainer(container, ...)
  274.     return GUI.addChildToContainer(container, GUI.container(...), GUI.objectTypes.container)
  275. end
  276.  
  277. -- Add image object to container
  278. local function addImageObjectToContainer(container, ...)
  279.     return GUI.addChildToContainer(container, GUI.image(...), GUI.objectTypes.image)
  280. end
  281.  
  282. -- Add image object to container
  283. local function addTabBarObjectToContainer(container, ...)
  284.     return GUI.addChildToContainer(container, GUI.tabBar(...), GUI.objectTypes.tabBar)
  285. end
  286.  
  287. -- Add InputTextBox object to container
  288. local function addInputTextBoxObjectToContainer(container, ...)
  289.     return GUI.addChildToContainer(container, GUI.inputTextBox(...), GUI.objectTypes.inputTextBox)
  290. end
  291.  
  292. -- Add TextBox object to container
  293. local function addTextBoxObjectToContainer(container, ...)
  294.     return GUI.addChildToContainer(container, GUI.textBox(...), GUI.objectTypes.textBox)
  295. end
  296.  
  297. -- Add Horizontal Slider object to container
  298. local function addHorizontalSliderObjectToContainer(container, ...)
  299.     return GUI.addChildToContainer(container, GUI.horizontalSlider(...), GUI.objectTypes.horizontalSlider)
  300. end
  301.  
  302. -- Add Progressbar object to container
  303. local function addProgressBarObjectToContainer(container, ...)
  304.     return GUI.addChildToContainer(container, GUI.progressBar(...), GUI.objectTypes.progressBar)
  305. end
  306.  
  307. -- Add Switch object to container
  308. local function addSwitchObjectToContainer(container, ...)
  309.     return GUI.addChildToContainer(container, GUI.switch(...), GUI.objectTypes.switch)
  310. end
  311.  
  312. -- Add Chart object to container
  313. local function addChartObjectToContainer(container, ...)
  314.     return GUI.addChildToContainer(container, GUI.chart(...), GUI.objectTypes.chart)
  315. end
  316.  
  317. -- Add ComboBox object to container
  318. local function addComboBoxObjectToContainer(container, ...)
  319.     return GUI.addChildToContainer(container, GUI.comboBox(...), GUI.objectTypes.comboBox)
  320. end
  321.  
  322. -- Recursively draw container's content including all children container's content
  323. local function drawContainerContent(container)
  324.     for objectIndex = 1, #container.children do
  325.         if not container.children[objectIndex].hidden then
  326.             container.children[objectIndex].x, container.children[objectIndex].y = container.children[objectIndex].localPosition.x + container.x - 1, container.children[objectIndex].localPosition.y + container.y - 1
  327.             if container.children[objectIndex].children then
  328.                 -- cyka blyad
  329.                 -- drawContainerContent(container.children[objectIndex])
  330.                 -- We use :draw() method against of recursive call. The reason is possible user-defined :draw() reimplementations
  331.                 container.children[objectIndex]:draw()
  332.             else
  333.                 -- if container.children[objectIndex].draw then
  334.                     container.children[objectIndex]:draw()
  335.                 -- else
  336.                 --  error("Container object with index " .. objectIndex .. " doesn't have :draw() method")
  337.                 -- end
  338.             end
  339.         end
  340.     end
  341.  
  342.     return container
  343. end
  344.  
  345. -- Delete every container's children object
  346. local function deleteContainersContent(container)
  347.     for objectIndex = 1, #container.children do container.children[objectIndex] = nil end
  348. end
  349.  
  350. -- Universal container to store any other objects like buttons, labels, etc
  351. function GUI.container(x, y, width, height)
  352.     local container = GUI.object(x, y, width, height)
  353.     container.children = {}
  354.     container.draw = drawContainerContent
  355.     container.getClickedObject = GUI.getClickedObject
  356.     container.deleteObjects = deleteContainersContent
  357.  
  358.     container.addChild = GUI.addChildToContainer
  359.     container.addObject = addEmptyObjectToContainer
  360.     container.addContainer = addContainerToContainer
  361.     container.addPanel = addPanelObjectToContainer
  362.     container.addLabel = addLabelObjectToContainer
  363.     container.addButton = addButtonObjectToContainer
  364.     container.addAdaptiveButton = addAdaptiveButtonObjectToContainer
  365.     container.addFramedButton = addFramedButtonObjectToContainer
  366.     container.addAdaptiveFramedButton = addAdaptiveFramedButtonObjectToContainer
  367.     container.addWindowActionButtons = addWindowActionButtonsObjectToContainer
  368.     container.addImage = addImageObjectToContainer
  369.     container.addTabBar = addTabBarObjectToContainer
  370.     container.addTextBox = addTextBoxObjectToContainer
  371.     container.addInputTextBox = addInputTextBoxObjectToContainer
  372.     container.addHorizontalSlider = addHorizontalSliderObjectToContainer
  373.     container.addSwitch = addSwitchObjectToContainer
  374.     container.addProgressBar = addProgressBarObjectToContainer
  375.     container.addChart = addChartObjectToContainer
  376.     container.addComboBox = addComboBoxObjectToContainer
  377.  
  378.     return container
  379. end
  380.  
  381. ----------------------------------------- Buttons -----------------------------------------
  382.  
  383. local function drawButton(object)
  384.     local text, textLength = objectTextLimit(object)
  385.  
  386.     local xText, yText = GUI.getAlignmentCoordinates(object, {width = textLength, height = 1})
  387.     local buttonColor = object.disabled and object.colors.disabled.background or (object.pressed and object.colors.pressed.background or object.colors.default.background)
  388.     local textColor = object.disabled and object.colors.disabled.text or (object.pressed and object.colors.pressed.text or object.colors.default.text)
  389.  
  390.     if buttonColor then
  391.         if object.buttonType == GUI.objectTypes.button then
  392.             buffer.square(object.x, object.y, object.width, object.height, buttonColor, textColor, " ")
  393.         else
  394.             buffer.frame(object.x, object.y, object.width, object.height, buttonColor)
  395.         end
  396.     end
  397.  
  398.     buffer.text(xText, yText, textColor, text)
  399.  
  400.     return object
  401. end
  402.  
  403. local function pressButton(object)
  404.     object.pressed = true
  405.     drawButton(object)
  406. end
  407.  
  408. local function releaseButton(object)
  409.     object.pressed = nil
  410.     drawButton(object)
  411. end
  412.  
  413. local function pressAndReleaseButton(object, pressTime)
  414.     pressButton(object)
  415.     buffer.draw()
  416.     os.sleep(pressTime or 0.2)
  417.     releaseButton(object)
  418.     buffer.draw()
  419. end
  420.  
  421. -- Создание таблицы кнопки со всеми необходимыми параметрами
  422. local function createButtonObject(buttonType, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  423.     local object = GUI.object(x, y, width, height)
  424.     object.colors = {
  425.         default = {
  426.             background = buttonColor,
  427.             text = textColor
  428.         },
  429.         pressed = {
  430.             background = buttonPressedColor,
  431.             text = textPressedColor
  432.         },
  433.         disabled = {
  434.             background = GUI.colors.disabled.background,
  435.             text = GUI.colors.disabled.text,
  436.         }
  437.     }
  438.     object.buttonType = buttonType
  439.     object.disabled = disabledState
  440.     object.text = text
  441.     object.press = pressButton
  442.     object.release = releaseButton
  443.     object.pressAndRelease = pressAndReleaseButton
  444.     object.draw = drawButton
  445.     object.setAlignment = GUI.setAlignment
  446.     object:setAlignment(GUI.alignment.horizontal.center, GUI.alignment.vertical.center)
  447.  
  448.     return object
  449. end
  450.  
  451. -- Кнопка фиксированных размеров
  452. function GUI.button(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  453.     return createButtonObject(GUI.objectTypes.button, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  454. end
  455.  
  456. -- Кнопка, подстраивающаяся под размер текста
  457. function GUI.adaptiveButton(x, y, xOffset, yOffset, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  458.     return createButtonObject(GUI.objectTypes.button, x, y, unicode.len(text) + xOffset * 2, yOffset * 2 + 1, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  459. end
  460.  
  461. -- Кнопка в рамке
  462. function GUI.framedButton(x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  463.     return createButtonObject(GUI.objectTypes.framedButton, x, y, width, height, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  464. end
  465.  
  466. function GUI.adaptiveFramedButton(x, y, xOffset, yOffset, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  467.     return createButtonObject(GUI.objectTypes.framedButton, x, y, unicode.len(text) + xOffset * 2, yOffset * 2 + 1, buttonColor, textColor, buttonPressedColor, textPressedColor, text, disabledState)
  468. end
  469.  
  470. ----------------------------------------- TabBar -----------------------------------------
  471.  
  472. local function drawTabBar(object)
  473.     for tab = 1, #object.tabs.children do
  474.         if tab == object.selectedTab then
  475.             object.tabs.children[tab].pressed = true
  476.         else
  477.             object.tabs.children[tab].pressed = false
  478.         end
  479.     end
  480.  
  481.     object:reimplementedDraw()
  482.     return object
  483. end
  484.  
  485. function GUI.tabBar(x, y, width, height, spaceBetweenElements, backgroundColor, textColor, backgroundSelectedColor, textSelectedColor, ...)
  486.     local elements, object = {...}, GUI.container(x, y, width, height)
  487.     object.selectedTab = 1
  488.     object.tabsWidth = 0; for elementIndex = 1, #elements do object.tabsWidth = object.tabsWidth + unicode.len(elements[elementIndex]) + 2 + spaceBetweenElements end; object.tabsWidth = object.tabsWidth - spaceBetweenElements
  489.     object.reimplementedDraw = object.draw
  490.     object.draw = drawTabBar
  491.  
  492.     object:addPanel(1, 1, object.width, object.height, backgroundColor).disableClicking = true
  493.     object.tabs = object:addContainer(1, 1, object.width, object.height)
  494.  
  495.     x = math.floor(width / 2 - object.tabsWidth / 2)
  496.     for elementIndex = 1, #elements do
  497.         local tab = object.tabs:addButton(x, 1, unicode.len(elements[elementIndex]) + 2, height, backgroundColor, textColor, backgroundSelectedColor, textSelectedColor, elements[elementIndex])
  498.         tab.type = GUI.objectTypes.tabBarTab
  499.         x = x + tab.width + spaceBetweenElements
  500.     end
  501.  
  502.     return object
  503. end
  504.  
  505. ----------------------------------------- Panel -----------------------------------------
  506.  
  507. local function drawPanel(object)
  508.     buffer.square(object.x, object.y, object.width, object.height, object.colors.background, 0x000000, " ", object.colors.transparency)
  509.     return object
  510. end
  511.  
  512. function GUI.panel(x, y, width, height, color, transparency)
  513.     local object = GUI.object(x, y, width, height)
  514.     object.colors = {background = color, transparency = transparency}
  515.     object.draw = drawPanel
  516.     return object
  517. end
  518.  
  519. ----------------------------------------- Label -----------------------------------------
  520.  
  521. local function drawLabel(object)
  522.     local text, textLength = objectTextLimit(object)
  523.     local xText, yText = GUI.getAlignmentCoordinates(object, {width = textLength, height = 1})
  524.     buffer.text(xText, yText, object.colors.text, text)
  525.     return object
  526. end
  527.  
  528. function GUI.label(x, y, width, height, textColor, text)
  529.     local object = GUI.object(x, y, width, height)
  530.     object.setAlignment = GUI.setAlignment
  531.     object:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top)
  532.     object.colors = {text = textColor}
  533.     object.text = text
  534.     object.draw = drawLabel
  535.     return object
  536. end
  537.  
  538. ----------------------------------------- Image -----------------------------------------
  539.  
  540. local function drawImage(object)
  541.     buffer.image(object.x, object.y, object.image)
  542.     return object
  543. end
  544.  
  545. function GUI.image(x, y, image)
  546.     local object = GUI.object(x, y, image.width, image.height)
  547.     object.image = image
  548.     object.draw = drawImage
  549.     return object
  550. end
  551.  
  552. ----------------------------------------- Window action buttons -----------------------------------------
  553.  
  554. local function drawWindowActionButton(object)
  555.     local background = buffer.get(object.x, object.y)
  556.     object.colors.default.background, object.colors.pressed.background, object.colors.disabled.background = background, background, background
  557.     object:reimplementedDraw()
  558.     return object
  559. end
  560.  
  561. function GUI.windowActionButtons(x, y, fatSymbol)
  562.     local symbol = fatSymbol and "⬤" or "●"
  563.    
  564.     local container = GUI.container(x, y, 5, 1)
  565.     container.close = container:addButton(1, 1, 1, 1, 0x000000, 0xFF4940, 0x000000, 0x992400, symbol)
  566.     container.minimize = container:addButton(3, 1, 1, 1, 0x000000, 0xFFB640, 0x000000, 0x996D00, symbol)
  567.     container.maximize = container:addButton(5, 1, 1, 1, 0x000000, 0x00B640, 0x000000, 0x006D40, symbol)
  568.  
  569.     container.close.reimplementedDraw, container.minimize.reimplementedDraw, container.maximize.reimplementedDraw = container.close.draw, container.minimize.draw, container.maximize.draw
  570.     container.close.draw, container.minimize.draw, container.maximize.draw = drawWindowActionButton, drawWindowActionButton, drawWindowActionButton
  571.  
  572.     return container
  573. end
  574.  
  575. ----------------------------------------- Dropdown Menu -----------------------------------------
  576.  
  577. local function drawDropDownMenuElement(object, itemIndex, isPressed)
  578.     local y = object.y + itemIndex * (object.spaceBetweenElements + 1) - 1
  579.     local yText = math.floor(y)
  580.    
  581.     if object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then
  582.         local textColor = object.items[itemIndex].disabled and object.colors.disabled.text or (object.items[itemIndex].color or object.colors.default.text)
  583.  
  584.         -- Нажатие
  585.         if isPressed then
  586.             buffer.square(object.x, y - object.spaceBetweenElements, object.width, object.spaceBetweenElements * 2 + 1, object.colors.pressed.background, object.colors.pressed.text, " ")
  587.             textColor = object.colors.pressed.text
  588.         end
  589.  
  590.         -- Основной текст
  591.         buffer.text(object.x + object.sidesOffset, yText, textColor, string.limit(object.items[itemIndex].text, object.width - object.sidesOffset * 2, false))
  592.         -- Шурткатикус
  593.         if object.items[itemIndex].shortcut then
  594.             buffer.text(object.x + object.width - unicode.len(object.items[itemIndex].shortcut) - object.sidesOffset, yText, textColor, object.items[itemIndex].shortcut)
  595.         end
  596.     else
  597.         -- Сепаратор
  598.         buffer.text(object.x, yText, object.colors.separator, string.rep("─", object.width))
  599.     end
  600. end
  601.  
  602. local function drawDropDownMenu(object)
  603.     buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ", object.colors.transparency)
  604.     if object.drawShadow then GUI.windowShadow(object.x, object.y, object.width, object.height, GUI.colors.contextMenu.transparency.shadow, true) end
  605.     for itemIndex = 1, #object.items do drawDropDownMenuElement(object, itemIndex, false) end
  606. end
  607.  
  608. local function showDropDownMenu(object)
  609.     local oldDrawLimit = buffer.getDrawLimit(); buffer.resetDrawLimit()
  610.     object.height = #object.items * (object.spaceBetweenElements + 1) + object.spaceBetweenElements
  611.  
  612.     local oldPixels = buffer.copy(object.x, object.y, object.width + 1, object.height + 1)
  613.     local function quit()
  614.         buffer.paste(object.x, object.y, oldPixels)
  615.         buffer.draw()
  616.         buffer.setDrawLimit(oldDrawLimit)
  617.     end
  618.  
  619.     drawDropDownMenu(object)
  620.     buffer.draw()
  621.  
  622.     while true do
  623.         local e = {event.pull()}
  624.         if e[1] == "touch" then
  625.             local objectFound = false
  626.             for itemIndex = 1, #object.items do
  627.                 if
  628.                     e[3] >= object.x and
  629.                     e[3] <= object.x + object.width - 1 and
  630.                     e[4] == object.y + itemIndex * (object.spaceBetweenElements + 1) - 1
  631.                 then
  632.                     objectFound = true
  633.                     if not object.items[itemIndex].disabled and object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then
  634.                         drawDropDownMenuElement(object, itemIndex, true)
  635.                         buffer.draw()
  636.                         os.sleep(0.2)
  637.                         quit()
  638.                         return object.items[itemIndex].text, itemIndex
  639.                     end
  640.                     break
  641.                 end
  642.             end
  643.  
  644.             if not objectFound then quit(); return end
  645.         end
  646.     end
  647. end
  648.  
  649. local function addDropDownMenuItem(object, text, disabled, shortcut, color)
  650.     local item = {}
  651.     item.type = GUI.dropDownMenuElementTypes.default
  652.     item.text = text
  653.     item.disabled = disabled
  654.     item.shortcut = shortcut
  655.     item.color = color
  656.  
  657.     table.insert(object.items, item)
  658.     return item
  659. end
  660.  
  661. local function addDropDownMenuSeparator(object)
  662.     local item = {type = GUI.dropDownMenuElementTypes.separator}
  663.     table.insert(object.items, item)
  664.     return item
  665. end
  666.  
  667. function GUI.dropDownMenu(x, y, width, spaceBetweenElements, backgroundColor, textColor, backgroundPressedColor, textPressedColor, disabledColor, separatorColor, transparency, items)
  668.     local object = GUI.object(x, y, width, 1)
  669.     object.colors = {
  670.         default = {
  671.             background = backgroundColor,
  672.             text = textColor
  673.         },
  674.         pressed = {
  675.             background = backgroundPressedColor,
  676.             text = textPressedColor
  677.         },
  678.         disabled = {
  679.             text = disabledColor
  680.         },
  681.         separator = separatorColor,
  682.         transparency = transparency
  683.     }
  684.     object.sidesOffset = 2
  685.     object.spaceBetweenElements = spaceBetweenElements
  686.     object.addSeparator = addDropDownMenuSeparator
  687.     object.addItem = addDropDownMenuItem
  688.     object.items = {}
  689.     if items then
  690.         for i = 1, #items do
  691.             object:addItem(items[i])
  692.         end
  693.     end
  694.     object.drawShadow = true
  695.     object.draw = drawDropDownMenu
  696.     object.show = showDropDownMenu
  697.     return object
  698. end
  699.  
  700. ----------------------------------------- Context Menu -----------------------------------------
  701.  
  702. local function showContextMenu(object)
  703.     -- Расчет ширины окна меню
  704.     local longestItem, longestShortcut = 0, 0
  705.     for itemIndex = 1, #object.items do
  706.         if object.items[itemIndex].type == GUI.dropDownMenuElementTypes.default then
  707.             longestItem = math.max(longestItem, unicode.len(object.items[itemIndex].text))
  708.             if object.items[itemIndex].shortcut then longestShortcut = math.max(longestShortcut, unicode.len(object.items[itemIndex].shortcut)) end
  709.         end
  710.     end
  711.     object.width = object.sidesOffset + longestItem + (longestShortcut > 0 and 3 + longestShortcut or 0) + object.sidesOffset
  712.     object.height = #object.items * (object.spaceBetweenElements + 1) + object.spaceBetweenElements
  713.  
  714.     -- А это чтоб за края экрана не лезло
  715.     if object.y + object.height >= buffer.screen.height then object.y = buffer.screen.height - object.height end
  716.     if object.x + object.width + 1 >= buffer.screen.width then object.x = buffer.screen.width - object.width - 1 end
  717.  
  718.     return object:reimplementedShow()
  719. end
  720.  
  721. function GUI.contextMenu(x, y, ...)
  722.     local argumentItems = {...}
  723.     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)
  724.  
  725.     -- Заполняем менюшку парашей
  726.     for itemIndex = 1, #argumentItems do
  727.         if argumentItems[itemIndex] == "-" then
  728.             object:addSeparator()
  729.         else
  730.             object:addItem(argumentItems[itemIndex][1], argumentItems[itemIndex][2], argumentItems[itemIndex][3], argumentItems[itemIndex][4])
  731.         end
  732.     end
  733.  
  734.     object.reimplementedShow = object.show
  735.     object.show = showContextMenu
  736.     object.selectedElement = nil
  737.     object.spaceBetweenElements = 0
  738.  
  739.     return object
  740. end
  741.  
  742. ----------------------------------------- Menu -----------------------------------------
  743.  
  744. function GUI.menu(x, y, width, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundTransparency, ...)
  745.     local elements = {...}
  746.     local menuObject = GUI.container(x, y, width, 1)
  747.     menuObject:addPanel(1, 1, menuObject.width, 1, backgroundColor, backgroundTransparency).disableClicking = true
  748.  
  749.     local x = 2
  750.     for elementIndex = 1, #elements do
  751.         local button = menuObject:addAdaptiveButton(x, 1, 1, 0, nil, elements[elementIndex][2] or textColor, elements[elementIndex][3] or backgroundPressedColor, elements[elementIndex][4] or textPressedColor, elements[elementIndex][1])
  752.         button.type = GUI.objectTypes.menuElement
  753.         x = x + button.width
  754.     end
  755.  
  756.     return menuObject
  757. end
  758.  
  759. ----------------------------------------- ProgressBar Object -----------------------------------------
  760.  
  761. local function drawProgressBar(object)
  762.     local activeWidth = math.floor(object.value * object.width / 100)
  763.     if object.thin then
  764.         buffer.text(object.x, object.y, object.colors.passive, string.rep("━", object.width))
  765.         buffer.text(object.x, object.y, object.colors.active, string.rep("━", activeWidth))
  766.     else
  767.         buffer.square(object.x, object.y, object.width, object.height, object.colors.passive)
  768.         buffer.square(object.x, object.y, activeWidth, object.height, object.colors.active)
  769.     end
  770.  
  771.     if object.showValue then
  772.         local stringValue = tostring((object.valuePrefix or "") .. object.value .. (object.valuePostfix or ""))
  773.         buffer.text(math.floor(object.x + object.width / 2 - unicode.len(stringValue) / 2), object.y + 1, object.colors.value, stringValue)
  774.     end
  775.  
  776.     return object
  777. end
  778.  
  779. function GUI.progressBar(x, y, width, activeColor, passiveColor, valueColor, value, thin, showValue, valuePrefix, valuePostfix)
  780.     local object = GUI.object(x, y, width, 1)
  781.     object.value = value
  782.     object.colors = {active = activeColor, passive = passiveColor, value = valueColor}
  783.     object.thin = thin
  784.     object.draw = drawProgressBar
  785.     object.showValue = showValue
  786.     object.valuePrefix = valuePrefix
  787.     object.valuePostfix = valuePostfix
  788.     return object
  789. end
  790.  
  791. ----------------------------------------- Other GUI elements -----------------------------------------
  792.  
  793. function GUI.windowShadow(x, y, width, height, transparency, thin)
  794.     transparency = transparency or 50
  795.     if thin then
  796.         buffer.square(x + width, y + 1, 1, height - 1, 0x000000, 0x000000, " ", transparency)
  797.         buffer.text(x + 1, y + height, 0x000000, string.rep("▀", width), transparency)
  798.         buffer.text(x + width, y, 0x000000, "▄", transparency)
  799.     else
  800.         buffer.square(x + width, y + 1, 2, height, 0x000000, 0x000000, " ", transparency)
  801.         buffer.square(x + 2, y + height, width - 2, 1, 0x000000, 0x000000, " ", transparency)
  802.     end
  803. end
  804.  
  805. ------------------------------------------------- Окна -------------------------------------------------------------------
  806.  
  807. -- Красивое окошко для отображения сообщения об ошибке. Аргумент errorWindowParameters может принимать следующие значения:
  808. -- local errorWindowParameters = {
  809. --   backgroundColor = 0x262626,
  810. --   textColor = 0xFFFFFF,
  811. --   truncate = 50,
  812. --   title = {color = 0xFF8888, text = "Ошибочка"}
  813. --   noAnimation = true,
  814. -- }
  815. function GUI.error(text, errorWindowParameters)
  816.     --Всякие константы, бла-бла
  817.     local backgroundColor = (errorWindowParameters and errorWindowParameters.backgroundColor) or 0x1b1b1b
  818.     local errorPixMap = {
  819.         {{0xffdb40       , 0xffffff,"#"}, {0xffdb40       , 0xffffff, "#"}, {backgroundColor, 0xffdb40, "▟"}, {backgroundColor, 0xffdb40, "▙"}, {0xffdb40       , 0xffffff, "#"}, {0xffdb40       , 0xffffff, "#"}},
  820.         {{0xffdb40       , 0xffffff,"#"}, {backgroundColor, 0xffdb40, "▟"}, {0xffdb40       , 0xffffff, " "}, {0xffdb40       , 0xffffff, " "}, {backgroundColor, 0xffdb40, "▙"}, {0xffdb40       , 0xffffff, "#"}},
  821.         {{backgroundColor, 0xffdb40,"▟"}, {0xffdb40       , 0xffffff, "c"}, {0xffdb40       , 0xffffff, "y"}, {0xffdb40       , 0xffffff, "k"}, {0xffdb40       , 0xffffff, "a"}, {backgroundColor, 0xffdb40, "▙"}},
  822.     }
  823.     local textColor = (errorWindowParameters and errorWindowParameters.textColor) or 0xFFFFFF
  824.     local buttonWidth = 12
  825.     local verticalOffset = 2
  826.     local minimumHeight = verticalOffset * 2 + #errorPixMap
  827.     local height = 0
  828.     local widthOfText = math.floor(buffer.screen.width * 0.5)
  829.  
  830.     --Ебемся с текстом, делаем его пиздатым во всех смыслах
  831.     if type(text) ~= "table" then
  832.         text = tostring(text)
  833.         text = (errorWindowParameters and errorWindowParameters.truncate) and unicode.sub(text, 1, errorWindowParameters.truncate) or text
  834.         text = { text }
  835.     end
  836.     text = string.wrap(text, widthOfText)
  837.  
  838.  
  839.     --Ебашим высоту правильнуюe
  840.     height = verticalOffset * 2 + #text + 1
  841.     if errorWindowParameters and errorWindowParameters.title then height = height + 2 end
  842.     if height < minimumHeight then height = minimumHeight end
  843.  
  844.     --Ебашим стартовые коорды отрисовки
  845.     local x, y = math.ceil(buffer.screen.width / 2 - widthOfText / 2), math.ceil(buffer.screen.height / 2 - height / 2)
  846.     local OKButton = {}
  847.     local oldPixels = buffer.copy(1, y, buffer.screen.width, height)
  848.  
  849.     --Отрисовочка
  850.     local function draw()
  851.         local yPos = y
  852.         --Подложка
  853.         buffer.square(1, yPos, buffer.screen.width, height, backgroundColor, 0x000000); yPos = yPos + verticalOffset
  854.         buffer.customImage(x - #errorPixMap[1] - 3, yPos, errorPixMap)
  855.         --Титл, епта!
  856.         if errorWindowParameters and errorWindowParameters.title then buffer.text(x, yPos, errorWindowParameters.title.color, errorWindowParameters.title.text); yPos = yPos + 2 end
  857.         --Текстус
  858.         for i = 1, #text do buffer.text(x, yPos, textColor, text[i]); yPos = yPos + 1 end; yPos = yPos + 1
  859.         --Кнопачка
  860.         OKButton = GUI.button(x + widthOfText - buttonWidth, y + height - 2, buttonWidth, 1, 0x3392FF, 0xFFFFFF, 0xFFFFFF, 0x262626, "OK"):draw()
  861.         --Атрисовачка
  862.         buffer.draw()
  863.     end
  864.  
  865.     --Графонистый выход
  866.     local function quit()
  867.         OKButton:pressAndRelease(0.2)
  868.         buffer.paste(1, y, oldPixels)
  869.         buffer.draw()
  870.     end
  871.  
  872.     --Онимацыя
  873.     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
  874.     draw()
  875.  
  876.     --Анализ говнища
  877.     while true do
  878.         local e = {event.pull()}
  879.         if e[1] == "key_down" then
  880.             if e[4] == 28 then
  881.                 quit(); return
  882.             end
  883.         elseif e[1] == "touch" then
  884.             if OKButton:isClicked(e[3], e[4]) then
  885.                 quit(); return
  886.             end
  887.         end
  888.     end
  889. end
  890.  
  891. ----------------------------------------- Universal keyboard-input function -----------------------------------------
  892.  
  893. local function findValue(t, whatToSearch)
  894.     if type(t) ~= "table" then return end
  895.     for key, value in pairs(t) do
  896.         if type(key) == "string" and string.match(key, "^" .. whatToSearch) then
  897.             local valueType, postfix = type(value), ""
  898.             if valueType == "function" or (valueType == "table" and getmetatable(value) and getmetatable(value).__call) then
  899.                 postfix = "()"
  900.             elseif valueType == "table" then
  901.                 postfix = "."
  902.             end
  903.             return key .. postfix
  904.         end
  905.     end
  906. end
  907.  
  908. local function findTable(whereToSearch, t, whatToSearch)
  909.     local beforeFirstDot = string.match(whereToSearch, "^[^%.]+%.")
  910.     -- Если вообще есть таблица, где надо искать
  911.     if beforeFirstDot then
  912.         beforeFirstDot = unicode.sub(beforeFirstDot, 1, -2)
  913.         if t[beforeFirstDot] then
  914.             return findTable(unicode.sub(whereToSearch, unicode.len(beforeFirstDot) + 2, -1), t[beforeFirstDot], whatToSearch)
  915.         else
  916.             -- Кароч, слушай суда: вот в эту зону хуйня может зайти толька
  917.             -- тагда, кагда ты вручную ебенишь массив вида "abc.cda.blabla.test"
  918.             -- без автозаполнения, т.е. он МОЖЕТ быть неверным, однако прога все
  919.             -- равно проверяет на верность, и вот если НИ ХУЯ такого говнища типа
  920.             -- ... .blabla не существует, то интерхпретатор захуяривается СУДЫ
  921.             -- И ЧТОБ БОЛЬШЕ ВОПРОСОВ НЕ ЗАДАВАЛ!11!
  922.         end
  923.     -- Или если таблиц либо ваще нету, либо рекурсия суда вон вошла
  924.     else
  925.         return findValue(t[whereToSearch], whatToSearch)
  926.     end
  927. end
  928.  
  929. local function autocompleteVariables(sourceText)
  930.     local varPath = string.match(sourceText, "[a-zA-Z0-9%.%_]+$")
  931.     if varPath then
  932.         local prefix = string.sub(sourceText, 1, -unicode.len(varPath) - 1)
  933.         local whereToSearch = string.match(varPath, "[a-zA-Z0-9%.%_]+%.")
  934.        
  935.         if whereToSearch then
  936.             whereToSearch = unicode.sub(whereToSearch, 1, -2)
  937.             local findedTable = findTable(whereToSearch, _G, unicode.sub(varPath, unicode.len(whereToSearch) + 2, -1))
  938.             return findedTable and prefix .. whereToSearch .. "." .. findedTable or sourceText
  939.         else
  940.             local findedValue = findValue(_G, varPath)
  941.             return findedValue and prefix .. findedValue or sourceText
  942.         end
  943.     else
  944.         return sourceText
  945.     end
  946. end
  947.  
  948. -- local inputProperties = {
  949. --  --Метод, получающий на вход текущий текст и проверяющий вводимые данные. В случае успеха должен возвращать true
  950. --  validator = function(text) if string.match(text, "^%d+$") then return true end
  951. --  -- Автоматически очищает текстовое поле при начале ввода информации в него.
  952. --  -- Если при окончании ввода тексет не будет соответствовать методу validator, указанному выше,
  953. --  -- то будет возвращено исходное значение текста (не очищенное)
  954. --  eraseTextWhenInputBegins = true
  955. --  --Отключает символ многоточия при выходе за пределы текстового поля
  956. --  disableDots = true,
  957. --  --Попросту отрисовывает всю необходимую информацию без активации нажатия на клавиши
  958. --  justDrawNotEvent = true,
  959. --  --Задержка между миганимем курсора
  960. --  cursorBlinkDelay = 1.5,
  961. --  --Цвет курсора
  962. --  cursorColor = 0xFF7777,
  963. --  --Символ, используемый для отрисовки курсора
  964. --  cursorSymbol = "▌",
  965. --  --Символ-маскировщик, на который будет визуально заменен весь вводимый текст. Полезно для полей ввода пароля
  966. --  maskTextWithSymbol = "*",
  967. --  --Активация подсветки Lua-синтаксиса
  968. --  highlightLuaSyntax = true,
  969. --  -- Активация автозаполнения названий переменных по нажатию клавиши Tab
  970. --  autocompleteVariables = true,
  971. -- }
  972.  
  973. function GUI.input(x, y, width, foreground, startText, inputProperties)
  974.     inputProperties = inputProperties or {}
  975.     if not (y >= buffer.drawLimit.y and y <= buffer.drawLimit.y2) then return startText end
  976.  
  977.     local text = startText
  978.     if not inputProperties.justDrawNotEvent and inputProperties.eraseTextWhenInputBegins then text = "" end
  979.     local textLength = unicode.len(text)
  980.     local cursorBlinkState = false
  981.     local cursorBlinkDelay = inputProperties.cursorBlinkDelay and inputProperties.cursorBlinkDelay or 0.5
  982.     local cursorColor = inputProperties.cursorColor and inputProperties.cursorColor or 0x00A8FF
  983.     local cursorSymbol = inputProperties.cursorSymbol and inputProperties.cursorSymbol or "┃"
  984.    
  985.     local oldPixels = {}; for i = x, x + width - 1 do table.insert(oldPixels, { buffer.get(i, y) }) end
  986.  
  987.     local function drawOldPixels()
  988.         for i = 1, #oldPixels do buffer.set(x + i - 1, y, oldPixels[i][1], oldPixels[i][2], oldPixels[i][3]) end
  989.     end
  990.  
  991.     local function getTextLength()
  992.         textLength = unicode.len(text)
  993.     end
  994.  
  995.     local function textFormat()
  996.         local formattedText = text
  997.  
  998.         if inputProperties.maskTextWithSymbol then
  999.             formattedText = string.rep(inputProperties.maskTextWithSymbol or "*", textLength)
  1000.         end
  1001.  
  1002.         if textLength > width then
  1003.             if inputProperties.disableDots then
  1004.                 formattedText = unicode.sub(formattedText, -width, -1)
  1005.             else
  1006.                 formattedText = "…" .. unicode.sub(formattedText, -width + 1, -1)
  1007.             end
  1008.         end
  1009.  
  1010.         return formattedText
  1011.     end
  1012.  
  1013.     local function draw()
  1014.         drawOldPixels()
  1015.         if inputProperties.highlightLuaSyntax then
  1016.             require("syntax").highlightString(x, y, textFormat(), 1, width)
  1017.         else
  1018.             buffer.text(x, y, foreground, textFormat())
  1019.         end
  1020.  
  1021.         if cursorBlinkState then
  1022.             local cursorPosition = textLength < width and x + textLength or x + width - 1
  1023.             local bg = buffer.get(cursorPosition, y)
  1024.             buffer.set(cursorPosition, y, bg, cursorColor, cursorSymbol)
  1025.         end
  1026.  
  1027.         if not inputProperties.justDrawNotEvent then buffer.draw() end
  1028.     end
  1029.  
  1030.     local function backspace()
  1031.         if unicode.len(text) > 0 then text = unicode.sub(text, 1, -2); getTextLength(); draw() end
  1032.     end
  1033.  
  1034.     local function quit()
  1035.         cursorBlinkState = false
  1036.         if inputProperties.validator and not inputProperties.validator(text) then
  1037.             text = startText
  1038.             draw()
  1039.             return startText
  1040.         end
  1041.         draw()
  1042.         return text
  1043.     end
  1044.  
  1045.     draw()
  1046.  
  1047.     if inputProperties.justDrawNotEvent then return startText end
  1048.  
  1049.     while true do
  1050.         local e = { event.pull(cursorBlinkDelay) }
  1051.         if e[1] == "key_down" then
  1052.             if e[4] == 14 then
  1053.                 backspace()
  1054.             elseif e[4] == 28 then
  1055.                 return quit()
  1056.             elseif e[4] == 15 then
  1057.                 if inputProperties.autocompleteVariables then
  1058.                     text = autocompleteVariables(text); getTextLength(); draw()
  1059.                 end
  1060.             else
  1061.                 local symbol = unicode.char(e[3])
  1062.                 if not keyboard.isControl(e[3]) then
  1063.                     text = text .. symbol
  1064.                     getTextLength()
  1065.                     draw()
  1066.                 end
  1067.             end
  1068.         elseif e[1] == "clipboard" then
  1069.             text = text .. e[3]
  1070.             getTextLength()
  1071.             draw()
  1072.         elseif e[1] == "touch" then
  1073.             return quit()
  1074.         else
  1075.             cursorBlinkState = not cursorBlinkState
  1076.             draw()
  1077.         end
  1078.     end
  1079. end
  1080.  
  1081. ----------------------------------------- Input Text Box object -----------------------------------------
  1082.  
  1083. local function drawInputTextBox(object, isFocused)
  1084.     local background = isFocused and object.colors.focused.background or object.colors.default.background
  1085.     local foreground = isFocused and object.colors.focused.text or object.colors.default.text
  1086.     local y = math.floor(object.y + object.height / 2)
  1087.     local text = isFocused and (object.text or "") or (object.text or object.placeholderText or "")
  1088.  
  1089.     if background then buffer.square(object.x, object.y, object.width, object.height, background, foreground, " ") end
  1090.     local resultText = GUI.input(object.x + 1, y, object.width - 2, foreground, text, {
  1091.         justDrawNotEvent = not isFocused,
  1092.         highlightLuaSyntax = isFocused and object.highlightLuaSyntax or nil,
  1093.         autocompleteVariables = object.autocompleteVariables or nil,
  1094.         maskTextWithSymbol = object.textMask or nil,
  1095.         validator = object.validator or nil,
  1096.         eraseTextWhenInputBegins = object.eraseTextOnFocus or nil,
  1097.     })
  1098.     object.text = isFocused and resultText or object.text
  1099. end
  1100.  
  1101. local function inputTextBoxBeginInput(object)
  1102.     drawInputTextBox(object, true)
  1103.     if object.text == "" then object.text = nil; drawInputTextBox(object, false); buffer.draw() end
  1104. end
  1105.  
  1106. function GUI.inputTextBox(x, y, width, height, inputTextBoxColor, textColor, inputTextBoxFocusedColor, textFocusedColor, text, placeholderText, maskTextWithSymbol, eraseTextOnFocus)
  1107.     local object = GUI.object(x, y, width, height)
  1108.     object.colors = {
  1109.         default = {
  1110.             background = inputTextBoxColor,
  1111.             text = textColor
  1112.         },
  1113.         focused = {
  1114.             background = inputTextBoxFocusedColor,
  1115.             text = textFocusedColor
  1116.         }
  1117.     }
  1118.     object.text = text
  1119.     object.placeholderText = placeholderText
  1120.     object.isClicked = isObjectClicked
  1121.     object.draw = drawInputTextBox
  1122.     object.input = inputTextBoxBeginInput
  1123.     object.eraseTextOnFocus = eraseTextOnFocus
  1124.     object.textMask = maskTextWithSymbol
  1125.     return object
  1126. end
  1127.  
  1128. ----------------------------------------- Text Box object -----------------------------------------
  1129.  
  1130. local function drawTextBox(object)
  1131.     if object.colors.background then buffer.square(object.x, object.y, object.width, object.height, object.colors.background, object.colors.text, " ") end
  1132.     local xPos, yPos = GUI.getAlignmentCoordinates(object, {width = 1, height = object.height - object.offset.vertical * 2})
  1133.     local lineLimit = object.width - object.offset.horizontal * 2
  1134.     for line = object.currentLine, object.currentLine + object.height - 1 do
  1135.         if object.lines[line] then
  1136.             local lineType, text, textColor = type(object.lines[line])
  1137.             if lineType == "table" then
  1138.                 text, textColor = string.limit(object.lines[line].text, lineLimit), object.lines[line].color
  1139.             elseif lineType == "string" then
  1140.                 text, textColor = string.limit(object.lines[line], lineLimit), object.colors.text
  1141.             else
  1142.                 error("Unknown TextBox line type: " .. tostring(lineType))
  1143.             end
  1144.  
  1145.             xPos = GUI.getAlignmentCoordinates(
  1146.                 {
  1147.                     x = object.x + object.offset.horizontal,
  1148.                     y = object.y + object.offset.vertical,
  1149.                     width = object.width - object.offset.horizontal * 2,
  1150.                     height = object.height - object.offset.vertical * 2,
  1151.                     alignment = object.alignment
  1152.                 },
  1153.                 {width = unicode.len(text), height = object.height}
  1154.             )
  1155.             buffer.text(xPos, yPos, textColor, text)
  1156.             yPos = yPos + 1
  1157.         else
  1158.             break
  1159.         end
  1160.     end
  1161.  
  1162.     return object
  1163. end
  1164.  
  1165. local function scrollDownTextBox(object, count)
  1166.     count = count or 1
  1167.     local maxCountAvailableToScroll = #object.lines - object.height - object.currentLine + 1
  1168.     count = math.min(count, maxCountAvailableToScroll)
  1169.     if #object.lines >= object.height and object.currentLine < #object.lines - count then
  1170.         object.currentLine = object.currentLine + count
  1171.     end
  1172.     return object
  1173. end
  1174.  
  1175. local function scrollUpTextBox(object, count)
  1176.     count = count or 1
  1177.     if object.currentLine > count and object.currentLine >= 1 then object.currentLine = object.currentLine - count end
  1178.     return object
  1179. end
  1180.  
  1181. local function scrollToStartTextBox(object)
  1182.     object.currentLine = 1
  1183.     return object
  1184. end
  1185.  
  1186. local function scrollToEndTextBox(object)
  1187.     object.currentLine = #lines
  1188.     return object
  1189. end
  1190.  
  1191. function GUI.textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset)
  1192.     local object = GUI.object(x, y, width, height)
  1193.     object.colors = { text = textColor, background = backgroundColor }
  1194.     object.setAlignment = GUI.setAlignment
  1195.     object:setAlignment(GUI.alignment.horizontal.left, GUI.alignment.vertical.top)
  1196.     object.lines = lines
  1197.     object.currentLine = currentLine or 1
  1198.     object.draw = drawTextBox
  1199.     object.scrollUp = scrollUpTextBox
  1200.     object.scrollDown = scrollDownTextBox
  1201.     object.scrollToStart = scrollToStartTextBox
  1202.     object.scrollToEnd = scrollToEndTextBox
  1203.     object.offset = {horizontal = horizontalOffset or 0, vertical = verticalOffset or 0}
  1204.  
  1205.     return object
  1206. end
  1207.  
  1208. ----------------------------------------- Horizontal Slider Object -----------------------------------------
  1209.  
  1210. local function drawHorizontalSlider(object)
  1211.     -- На всякий случай делаем значение не меньше минимального и не больше максимального
  1212.     object.value = math.min(math.max(object.value, object.minimumValue), object.maximumValue)
  1213.  
  1214.     -- Отображаем максимальное и минимальное значение, если требуется
  1215.     if object.showMaximumAndMinimumValues then
  1216.         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))
  1217.         buffer.text(object.x - unicode.len(stringMinimumValue) - 1, object.y, object.colors.value, stringMinimumValue)
  1218.         buffer.text(object.x + object.width + 1, object.y, object.colors.value, stringMaximumValue)
  1219.     end
  1220.  
  1221.     -- А еще текущее значение рисуем, если хочется нам
  1222.     if object.currentValuePrefix or object.currentValuePostfix then
  1223.         local stringCurrentValue = (object.currentValuePrefix or "") .. (object.roundValues and math.floor(object.value) or math.roundToDecimalPlaces(object.value, 2)) .. (object.currentValuePostfix or "")
  1224.         buffer.text(math.floor(object.x + object.width / 2 - unicode.len(stringCurrentValue) / 2), object.y + 1, object.colors.value, stringCurrentValue)
  1225.     end
  1226.  
  1227.     -- Рисуем сам слайдер
  1228.     local activeWidth = math.floor(object.width - ((object.maximumValue - object.value) * object.width / (object.maximumValue - object.minimumValue)))
  1229.     buffer.text(object.x, object.y, object.colors.passive, string.rep("━", object.width))
  1230.     buffer.text(object.x, object.y, object.colors.active, string.rep("━", activeWidth))
  1231.     buffer.square(object.x + activeWidth - 1, object.y, 2, 1, object.colors.pipe, 0x000000, " ")
  1232.  
  1233.     return object
  1234. end
  1235.  
  1236. function GUI.horizontalSlider(x, y, width, activeColor, passiveColor, pipeColor, valueColor, minimumValue, maximumValue, value, showMaximumAndMinimumValues, currentValuePrefix, currentValuePostfix)
  1237.     local object = GUI.object(x, y, width, 1)
  1238.     object.colors = {active = activeColor, passive = passiveColor, pipe = pipeColor, value = valueColor}
  1239.     object.draw = drawHorizontalSlider
  1240.     object.minimumValue = minimumValue
  1241.     object.maximumValue = maximumValue
  1242.     object.value = value
  1243.     object.showMaximumAndMinimumValues = showMaximumAndMinimumValues
  1244.     object.currentValuePrefix = currentValuePrefix
  1245.     object.currentValuePostfix = currentValuePostfix
  1246.     object.roundValues = false
  1247.     return object
  1248. end
  1249.  
  1250. ----------------------------------------- Switch object -----------------------------------------
  1251.  
  1252. local function drawSwitch(object)
  1253.     local pipeWidth = object.height * 2
  1254.     local pipePosition, backgroundColor
  1255.     if object.state then pipePosition, backgroundColor = object.x + object.width - pipeWidth, object.colors.active else pipePosition, backgroundColor = object.x, object.colors.passive end
  1256.     buffer.square(object.x, object.y, object.width, object.height, backgroundColor, 0x000000, " ")
  1257.     buffer.square(pipePosition, object.y, pipeWidth, object.height, object.colors.pipe, 0x000000, " ")
  1258.     return object
  1259. end
  1260.  
  1261. function GUI.switch(x, y, width, activeColor, passiveColor, pipeColor, state)
  1262.     local object = GUI.object(x, y, width, 1)
  1263.     object.colors = {active = activeColor, passive = passiveColor, pipe = pipeColor, value = valueColor}
  1264.     object.draw = drawSwitch
  1265.     object.state = state or false
  1266.     return object
  1267. end
  1268.  
  1269. ----------------------------------------- Chart object -----------------------------------------
  1270.  
  1271. local function drawChart(object)
  1272.     -- Ебошем пездатые оси
  1273.     for i = object.y, object.y + object.height - 2 do buffer.text(object.x, i, object.colors.axis, "│") end
  1274.     buffer.text(object.x + 1, object.y + object.height - 1, object.colors.axis, string.rep("─", object.width - 1))
  1275.     buffer.text(object.x, object.y + object.height - 1, object.colors.axis, "└")
  1276.  
  1277.     if #object.values > 1 then
  1278.         local oldDrawLimit = buffer.getDrawLimit()
  1279.         buffer.setDrawLimit(object.x, object.y, object.width, object.height)
  1280.        
  1281.         local delta, fieldWidth, fieldHeight = object.maximumValue - object.minimumValue, object.width - 2, object.height - 1
  1282.  
  1283.         -- Рисуем линии значений
  1284.         local roundValues = object.maximumValue > 10
  1285.         local step = 0.2 * fieldHeight
  1286.         for i = step, fieldHeight, step do
  1287.             local value = object.minimumValue + delta * (i / fieldHeight)
  1288.             local stringValue = roundValues and tostring(math.floor(value)) or math.doubleToString(value, 1)
  1289.             buffer.text(object.x + 1, math.floor(object.y + fieldHeight - i), object.colors.value, string.rep("─", object.width - unicode.len(stringValue) - 2) .. " " .. stringValue)
  1290.         end
  1291.  
  1292.         -- Рисуем графек, йопта
  1293.         local function getDotPosition(valueIndex)
  1294.             return
  1295.                 object.x + math.round((fieldWidth * (valueIndex - 1) / (#object.values - 1))) + 1,
  1296.                 object.y + math.round(((fieldHeight - 1) * (object.maximumValue - object.values[valueIndex]) / delta))
  1297.         end
  1298.  
  1299.         local x, y = getDotPosition(1)
  1300.         for valueIndex = 2, #object.values do
  1301.             local xNew, yNew = getDotPosition(valueIndex)
  1302.             buffer.semiPixelLine(x, y * 2, xNew, yNew * 2, object.colors.chart)
  1303.             x, y = xNew, yNew
  1304.         end
  1305.  
  1306.         buffer.setDrawLimit(oldDrawLimit)
  1307.     end
  1308.  
  1309.     -- Дорисовываем названия осей
  1310.     if object.axisNames.y then buffer.text(object.x + 1, object.y, object.colors.axis, object.axisNames.y) end
  1311.     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
  1312. end
  1313.  
  1314. function GUI.chart(x, y, width, height, axisColor, axisValueColor, chartColor, xAxisName, yAxisName, minimumValue, maximumValue, values)
  1315.     if minimumValue >= maximumValue then error("Chart's minimum value can't be >= maximum value!") end
  1316.     local object = GUI.object(x, y, width, height)
  1317.     object.colors = {axis = axisColor, chart = chartColor, value = axisValueColor}
  1318.     object.draw = drawChart
  1319.     object.values = values
  1320.     object.minimumValue = minimumValue
  1321.     object.maximumValue = maximumValue
  1322.     object.axisNames = {x = xAxisName, y = yAxisName}
  1323.     return object
  1324. end
  1325.  
  1326. ----------------------------------------- Combo Box Object -----------------------------------------
  1327.  
  1328. local function drawComboBox(object)
  1329.     buffer.square(object.x, object.y, object.width, object.height, object.colors.default.background)
  1330.     local x, y, limit, arrowSize = object.x + 1, math.floor(object.y + object.height / 2), object.width - 5, object.height
  1331.     buffer.text(x, y, object.colors.default.text, string.limit(object.items[object.currentItem].text, limit, false))
  1332.     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()
  1333. end
  1334.  
  1335. local function selectComboBoxItem(object)
  1336.     object.state = true
  1337.     object:draw()
  1338.  
  1339.     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)
  1340.     dropDownMenu.items = object.items
  1341.     dropDownMenu.sidesOffset = 1
  1342.     local _, itemIndex = dropDownMenu:show()
  1343.  
  1344.     object.currentItem = itemIndex or object.currentItem
  1345.     object.state = false
  1346.     object:draw()
  1347.     buffer.draw()
  1348. end
  1349.  
  1350. function GUI.comboBox(x, y, width, height, backgroundColor, textColor, arrowBackgroundColor, arrowTextColor, items)
  1351.     local object = GUI.object(x, y, width, height)
  1352.     object.colors = {
  1353.         default = {
  1354.             background = backgroundColor,
  1355.             text = textColor
  1356.         },
  1357.         pressed = {
  1358.             background = GUI.colors.contextMenu.pressed.background,
  1359.             text = GUI.colors.contextMenu.pressed.text
  1360.         },
  1361.         arrow = {
  1362.             background = arrowBackgroundColor,
  1363.             text = arrowTextColor
  1364.         }
  1365.     }
  1366.     object.items = {}
  1367.     object.currentItem = 1
  1368.     object.addItem = addDropDownMenuItem
  1369.     object.addSeparator = addDropDownMenuSeparator
  1370.     if items then
  1371.         for i = 1, #items do
  1372.             object:addItem(items[i])
  1373.         end
  1374.     end
  1375.     object.draw = drawComboBox
  1376.     object.selectItem = selectComboBoxItem
  1377.     object.state = false
  1378.     return object
  1379. end
  1380.  
  1381. --------------------------------------------------------------------------------------------------------------------------------
  1382.  
  1383. -- buffer.start()
  1384. -- buffer.clear(0x1b1b1b)
  1385.  
  1386. -- local comboBox = GUI.comboBox(2, 2, 30, 1, 0xFFFFFF, 0x262626, 0xDDDDDD, 0x262626, {"PIC", "RAW", "PNG", "JPG"})
  1387. -- comboBox:selectItem()
  1388.  
  1389. -- buffer.draw()
  1390.  
  1391. -- GUI.chart(2, 10, 40, 20, 0xFFFFFF, 0xBBBBBB, 0xFFDB40, "t", "EU", 0, 2, {
  1392. --  0.5,
  1393. --  0.12
  1394. -- }):draw()
  1395.  
  1396. -- local menu = GUI.dropDownMenu(2, 2, 40, 1, 0xFFFFFF, 0x000000, 0xFFDB40, 0xFFFFFF, 0x999999, 0x777777, 50)
  1397. -- menu:addItem("New")
  1398. -- menu:addItem("Open")
  1399. -- menu:addSeparator()
  1400. -- menu:addItem("Save")
  1401. -- menu:addItem("Save as")
  1402. -- menu:show()
  1403.  
  1404. -- GUI.contextMenu(2, 2, {"Hello"}, {"World"}, "-", {"You are the"}, {"Best of best", false, "^S"}, {"And bestest yopta"}):show()
  1405.  
  1406.  
  1407. --------------------------------------------------------------------------------------------------------------------------------
  1408.  
  1409. return GUI
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement