SHARE
TWEET

Untitled

OstojaTheGamer Nov 14th, 2019 16 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.  
  2. require("advancedLua")
  3. local component = require("component")
  4. local computer = require("computer")
  5. local keyboard = require("keyboard")
  6. local filesystem = require("filesystem")
  7. local unicode = require("unicode")
  8. local event = require("event")
  9. local color = require("color")
  10. local image = require("image")
  11. local buffer = require("doubleBuffering")
  12.  
  13. -----------------------------------------------------------------------------------------
  14.  
  15. local GUI = {
  16.     ALIGNMENT_HORIZONTAL_LEFT = 1,
  17.     ALIGNMENT_HORIZONTAL_CENTER = 2,
  18.     ALIGNMENT_HORIZONTAL_RIGHT = 3,
  19.     ALIGNMENT_VERTICAL_TOP = 4,
  20.     ALIGNMENT_VERTICAL_CENTER = 5,
  21.     ALIGNMENT_VERTICAL_BOTTOM = 6,
  22.  
  23.     DIRECTION_HORIZONTAL = 7,
  24.     DIRECTION_VERTICAL = 8,
  25.  
  26.     SIZE_POLICY_ABSOLUTE = 9,
  27.     SIZE_POLICY_RELATIVE = 10,
  28.  
  29.     IO_MODE_FILE = 11,
  30.     IO_MODE_DIRECTORY = 12,
  31.     IO_MODE_BOTH = 13,
  32.     IO_MODE_OPEN = 14,
  33.     IO_MODE_SAVE = 15,
  34.  
  35.     BUTTON_PRESS_DURATION = 0.2,
  36.     BUTTON_ANIMATION_DURATION = 0.2,
  37.     SWITCH_ANIMATION_DURATION = 0.3,
  38.     FILESYSTEM_DIALOG_ANIMATION_DURATION = 0.5,
  39.    
  40.     CONTEXT_MENU_SEPARATOR_COLOR = 0x878787,
  41.     CONTEXT_MENU_DEFAULT_TEXT_COLOR = 0x2D2D2D,
  42.     CONTEXT_MENU_DEFAULT_BACKGROUND_COLOR = 0xFFFFFF,
  43.     CONTEXT_MENU_PRESSED_BACKGROUND_COLOR = 0x3366CC,
  44.     CONTEXT_MENU_PRESSED_TEXT_COLOR = 0xFFFFFF,
  45.     CONTEXT_MENU_DISABLED_COLOR = 0x878787,
  46.     CONTEXT_MENU_BACKGROUND_TRANSPARENCY = 0.18,
  47.     CONTEXT_MENU_SHADOW_TRANSPARENCY = 0.4,
  48.  
  49.     BACKGROUND_CONTAINER_PANEL_COLOR = 0x0,
  50.     BACKGROUND_CONTAINER_TITLE_COLOR = 0xE1E1E1,
  51.     BACKGROUND_CONTAINER_PANEL_TRANSPARENCY = 0.3,
  52.  
  53.     WINDOW_BACKGROUND_PANEL_COLOR = 0xF0F0F0,
  54.     WINDOW_SHADOW_TRANSPARENCY = 0.6,
  55.     WINDOW_TITLE_BACKGROUND_COLOR = 0xE1E1E1,
  56.     WINDOW_TITLE_TEXT_COLOR = 0x2D2D2D,
  57.     WINDOW_TAB_BAR_DEFAULT_BACKGROUND_COLOR = 0x2D2D2D,
  58.     WINDOW_TAB_BAR_DEFAULT_TEXT_COLOR = 0xF0F0F0,
  59.     WINDOW_TAB_BAR_SELECTED_BACKGROUND_COLOR = 0xF0F0F0,
  60.     WINDOW_TAB_BAR_SELECTED_TEXT_COLOR = 0x2D2D2D,
  61.  
  62.     PALETTE_CONFIG_PATH = filesystem.path(getCurrentScript()) .. ".palette.cfg",
  63.  
  64.     LUA_SYNTAX_COLOR_SCHEME = {
  65.         background = 0x1E1E1E,
  66.         text = 0xE1E1E1,
  67.         strings = 0x99FF80,
  68.         loops = 0xFFFF98,
  69.         comments = 0x898989,
  70.         boolean = 0xFFDB40,
  71.         logic = 0xFFCC66,
  72.         numbers = 0x66DBFF,
  73.         functions = 0xFFCC66,
  74.         compares = 0xFFCC66,
  75.         lineNumbersBackground = 0x2D2D2D,
  76.         lineNumbersText = 0xC3C3C3,
  77.         scrollBarBackground = 0x2D2D2D,
  78.         scrollBarForeground = 0x5A5A5A,
  79.         selection = 0x4B4B4B,
  80.         indentation = 0x2D2D2D
  81.     },
  82.  
  83.     LUA_SYNTAX_PATTERNS = {
  84.         "[%.%,%>%<%=%~%+%-%*%/%^%#%%%&]", "compares", 0, 0,
  85.         "[^%a%d][%.%d]+[^%a%d]", "numbers", 1, 1,
  86.         "[^%a%d][%.%d]+$", "numbers", 1, 0,
  87.         "0x%w+", "numbers", 0, 0,
  88.         " not ", "logic", 0, 1,
  89.         " or ", "logic", 0, 1,
  90.         " and ", "logic", 0, 1,
  91.         "function%(", "functions", 0, 1,
  92.         "function%s[^%s%(%)%{%}%[%]]+%(", "functions", 9, 1,
  93.         "nil", "boolean", 0, 0,
  94.         "false", "boolean", 0, 0,
  95.         "true", "boolean", 0, 0,
  96.         " break$", "loops", 0, 0,
  97.         "elseif ", "loops", 0, 1,
  98.         "else[%s%;]", "loops", 0, 1,
  99.         "else$", "loops", 0, 0,
  100.         "function ", "loops", 0, 1,
  101.         "local ", "loops", 0, 1,
  102.         "return", "loops", 0, 0,
  103.         "until ", "loops", 0, 1,
  104.         "then", "loops", 0, 0,
  105.         "if ", "loops", 0, 1,
  106.         "repeat$", "loops", 0, 0,
  107.         " in ", "loops", 0, 1,
  108.         "for ", "loops", 0, 1,
  109.         "end[%s%;]", "loops", 0, 1,
  110.         "end$", "loops", 0, 0,
  111.         "do ", "loops", 0, 1,
  112.         "do$", "loops", 0, 0,
  113.         "while ", "loops", 0, 1,
  114.         "\'[^\']+\'", "strings", 0, 0,
  115.         "\"[^\"]+\"", "strings", 0, 0,
  116.         "%-%-.+", "comments", 0, 0,
  117.     },
  118. }
  119.  
  120. --------------------------------------------------------------------------------
  121.  
  122. function GUI.setAlignment(object, horizontalAlignment, verticalAlignment)
  123.     object.horizontalAlignment, object.verticalAlignment = horizontalAlignment, verticalAlignment
  124.    
  125.     return object
  126. end
  127.  
  128. function GUI.getAlignmentCoordinates(x, y, width1, height1, horizontalAlignment, verticalAlignment, width2, height2)
  129.     if horizontalAlignment == GUI.ALIGNMENT_HORIZONTAL_CENTER then
  130.         x = x + width1 / 2 - width2 / 2
  131.     elseif horizontalAlignment == GUI.ALIGNMENT_HORIZONTAL_RIGHT then
  132.         x = x + width1 - width2
  133.     elseif horizontalAlignment ~= GUI.ALIGNMENT_HORIZONTAL_LEFT then
  134.         error("Unknown horizontal alignment: " .. tostring(horizontalAlignment))
  135.     end
  136.  
  137.     if verticalAlignment == GUI.ALIGNMENT_VERTICAL_CENTER then
  138.         y = y + height1 / 2 - height2 / 2
  139.     elseif verticalAlignment == GUI.ALIGNMENT_VERTICAL_BOTTOM then
  140.         y = y + height1 - height2
  141.     elseif verticalAlignment ~= GUI.ALIGNMENT_VERTICAL_TOP then
  142.         error("Unknown vertical alignment: " .. tostring(verticalAlignment))
  143.     end
  144.  
  145.     return x, y
  146. end
  147.  
  148. function GUI.getMarginCoordinates(x, y, horizontalAlignment, verticalAlignment, horizontalMargin, verticalMargin)
  149.     if horizontalAlignment == GUI.ALIGNMENT_HORIZONTAL_RIGHT then
  150.         x = x - horizontalMargin
  151.     else
  152.         x = x + horizontalMargin
  153.     end
  154.    
  155.     if verticalAlignment == GUI.ALIGNMENT_VERTICAL_BOTTOM then
  156.         y = y - verticalMargin
  157.     else
  158.         y = y + verticalMargin
  159.     end
  160.  
  161.     return x, y
  162. end
  163.  
  164. --------------------------------------------------------------------------------
  165.  
  166. local function objectDraw(object)
  167.     return object
  168. end
  169.  
  170. function GUI.object(x, y, width, height)
  171.     return {
  172.         x = x,
  173.         y = y,
  174.         width = width,
  175.         height = height,
  176.         draw = objectDraw
  177.     }
  178. end
  179.  
  180. --------------------------------------------------------------------------------
  181.  
  182. local function containerObjectIndexOf(object)
  183.     if not object.parent then error("Object doesn't have a parent container") end
  184.  
  185.     for objectIndex = 1, #object.parent.children do
  186.         if object.parent.children[objectIndex] == object then
  187.             return objectIndex
  188.         end
  189.     end
  190. end
  191.  
  192. local function containerObjectMoveForward(object)
  193.     local objectIndex = containerObjectIndexOf(object)
  194.     if objectIndex < #object.parent.children then
  195.         object.parent.children[index], object.parent.children[index + 1] = object.parent.children[index + 1], object.parent.children[index]
  196.     end
  197.    
  198.     return object
  199. end
  200.  
  201. local function containerObjectMoveBackward(object)
  202.     local objectIndex = containerObjectIndexOf(object)
  203.     if objectIndex > 1 then
  204.         object.parent.children[objectIndex], object.parent.children[objectIndex - 1] = object.parent.children[objectIndex - 1], object.parent.children[objectIndex]
  205.     end
  206.    
  207.     return object
  208. end
  209.  
  210. local function containerObjectMoveToFront(object)
  211.     table.remove(object.parent.children, containerObjectIndexOf(object))
  212.     table.insert(object.parent.children, object)
  213.    
  214.     return object
  215. end
  216.  
  217. local function containerObjectMoveToBack(object)
  218.     table.remove(object.parent.children, containerObjectIndexOf(object))
  219.     table.insert(object.parent.children, 1, object)
  220.    
  221.     return object
  222. end
  223.  
  224. local function containerObjectRemove(object)
  225.     table.remove(object.parent.children, containerObjectIndexOf(object))
  226. end
  227.  
  228. local function containerObjectAnimationStart(animation, duration)
  229.     animation.position = 0
  230.     animation.duration = duration
  231.     animation.started = true
  232.     animation.startUptime = computer.uptime()
  233.  
  234.     computer.pushSignal("GUI", "animationStarted")
  235. end
  236.  
  237. local function containerObjectAnimationStop(animation)
  238.     animation.position = 0
  239.     animation.started = false
  240. end
  241.  
  242. local function containerObjectAnimationRemove(animation)
  243.     animation.removeLater = true
  244. end
  245.  
  246. local function containerObjectAddAnimation(object, frameHandler, onFinish)
  247.     local animation = {
  248.         object = object,
  249.         position = 0,
  250.         start = containerObjectAnimationStart,
  251.         stop = containerObjectAnimationStop,
  252.         remove = containerObjectAnimationRemove,
  253.         frameHandler = frameHandler,
  254.         onFinish = onFinish,
  255.     }
  256.  
  257.     object.firstParent.animations = object.firstParent.animations or {}
  258.     table.insert(object.firstParent.animations, animation)
  259.  
  260.     return animation
  261. end
  262.  
  263. local function containerAddChild(container, object, atIndex)
  264.     object.localX = object.x
  265.     object.localY = object.y
  266.     object.indexOf = containerObjectIndexOf
  267.     object.moveToFront = containerObjectMoveToFront
  268.     object.moveToBack = containerObjectMoveToBack
  269.     object.moveForward = containerObjectMoveForward
  270.     object.moveBackward = containerObjectMoveBackward
  271.     object.remove = containerObjectRemove
  272.     object.addAnimation = containerObjectAddAnimation
  273.  
  274.     local function updateFirstParent(object, firstParent)
  275.         object.firstParent = firstParent
  276.         if object.children then
  277.             for i = 1, #object.children do
  278.                 updateFirstParent(object.children[i], firstParent)
  279.             end
  280.         end
  281.     end
  282.  
  283.     object.parent = container
  284.     updateFirstParent(object, container.firstParent or container)
  285.  
  286.     if atIndex then
  287.         table.insert(container.children, atIndex, object)
  288.     else
  289.         table.insert(container.children, object)
  290.     end
  291.    
  292.     return object
  293. end
  294.  
  295. local function containerRemoveChildren(container, from, to)
  296.     from = from or 1
  297.     for objectIndex = from, to or #container.children do
  298.         table.remove(container.children, from)
  299.     end
  300. end
  301.  
  302. local function getRectangleIntersection(R1X1, R1Y1, R1X2, R1Y2, R2X1, R2Y1, R2X2, R2Y2)
  303.     if R2X1 <= R1X2 and R2Y1 <= R1Y2 and R2X2 >= R1X1 and R2Y2 >= R1Y1 then
  304.         return
  305.             math.max(R2X1, R1X1),
  306.             math.max(R2Y1, R1Y1),
  307.             math.min(R2X2, R1X2),
  308.             math.min(R2Y2, R1Y2)
  309.     else
  310.         return
  311.     end
  312. end
  313.  
  314. function containerDraw(container)
  315.     local R1X1, R1Y1, R1X2, R1Y2, child = buffer.getDrawLimit()
  316.     local intersectionX1, intersectionY1, intersectionX2, intersectionY2 = getRectangleIntersection(
  317.         R1X1,
  318.         R1Y1,
  319.         R1X2,
  320.         R1Y2,
  321.         container.x,
  322.         container.y,
  323.         container.x + container.width - 1,
  324.         container.y + container.height - 1
  325.     )
  326.  
  327.     if intersectionX1 then
  328.         buffer.setDrawLimit(intersectionX1, intersectionY1, intersectionX2, intersectionY2)
  329.        
  330.         for i = 1, #container.children do
  331.             child = container.children[i]
  332.            
  333.             if not child.hidden then
  334.                 child.x, child.y = container.x + child.localX - 1, container.y + child.localY - 1
  335.                 child:draw()
  336.             end
  337.         end
  338.  
  339.         buffer.setDrawLimit(R1X1, R1Y1, R1X2, R1Y2)
  340.     end
  341.  
  342.     return container
  343. end
  344.  
  345. function GUI.container(x, y, width, height)
  346.     local container = GUI.object(x, y, width, height)
  347.  
  348.     container.children = {}
  349.     container.passScreenEvents = true
  350.    
  351.     container.draw = containerDraw
  352.     container.removeChildren = containerRemoveChildren
  353.     container.addChild = containerAddChild
  354.    
  355.     return container
  356. end
  357.  
  358. function GUI.fullScreenContainer()
  359.     return GUI.container(1, 1, buffer.getResolution())
  360. end
  361.  
  362. --------------------------------------------------------------------------------
  363.  
  364. local function applicationStart(application, eventPullTimeout)
  365.     local animation, animationIndex, animationOnFinishMethodsIndex, animationOnFinishMethods, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32
  366.    
  367.     local function handle(isScreenEvent, currentContainer, intersectionX1, intersectionY1, intersectionX2, intersectionY2)
  368.         if
  369.             not isScreenEvent or
  370.             intersectionX1 and
  371.             e3 >= intersectionX1 and
  372.             e3 <= intersectionX2 and
  373.             e4 >= intersectionY1 and
  374.             e4 <= intersectionY2
  375.         then
  376.             local currentContainerPassed, child, newIntersectionX1, newIntersectionY1, newIntersectionX2, newIntersectionY2
  377.  
  378.             if isScreenEvent then
  379.                 if currentContainer.eventHandler and not currentContainer.disabled then
  380.                     currentContainer.eventHandler(application, currentContainer, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32)
  381.                 end
  382.  
  383.                 currentContainerPassed = not currentContainer.passScreenEvents
  384.             elseif currentContainer.eventHandler then
  385.                 currentContainer.eventHandler(application, currentContainer, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32)
  386.             end
  387.  
  388.             for i = #currentContainer.children, 1, -1 do
  389.                 child = currentContainer.children[i]
  390.  
  391.                 if not child.hidden then
  392.                     if child.children then
  393.                         newIntersectionX1, newIntersectionY1, newIntersectionX2, newIntersectionY2 = getRectangleIntersection(
  394.                             intersectionX1,
  395.                             intersectionY1,
  396.                             intersectionX2,
  397.                             intersectionY2,
  398.                             child.x,
  399.                             child.y,
  400.                             child.x + child.width - 1,
  401.                             child.y + child.height - 1
  402.                         )
  403.  
  404.                         if
  405.                             newIntersectionX1 and
  406.                             handle(
  407.                                 isScreenEvent,
  408.                                 child,
  409.                                 newIntersectionX1,
  410.                                 newIntersectionY1,
  411.                                 newIntersectionX2,
  412.                                 newIntersectionY2,
  413.                                 e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32
  414.                             )
  415.                         then
  416.                             return true
  417.                         end
  418.                     else
  419.                         if application.needConsume then
  420.                             application.needConsume = nil
  421.                             return true
  422.                         end
  423.  
  424.                         if isScreenEvent then
  425.                             if
  426.                                 e3 >= child.x and
  427.                                 e3 <= child.x + child.width - 1 and
  428.                                 e4 >= child.y and
  429.                                 e4 <= child.y + child.height - 1
  430.                             then
  431.                                 if child.eventHandler and not child.disabled then
  432.                                     child.eventHandler(application, child, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32)
  433.                                 end
  434.  
  435.                                 if not child.passScreenEvents then
  436.                                     return true
  437.                                 end
  438.                             end
  439.                         elseif child.eventHandler then
  440.                             child.eventHandler(application, child, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32)
  441.                         end
  442.                     end
  443.                 end
  444.             end
  445.  
  446.             if currentContainerPassed then
  447.                 return true
  448.             end
  449.         end
  450.     end
  451.  
  452.     application.eventPullTimeout = eventPullTimeout
  453.  
  454.     repeat
  455.         e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31, e32 = event.pull(application.animations and 0 or application.eventPullTimeout)
  456.        
  457.         handle(
  458.             e1 == "touch" or
  459.             e1 == "drag" or
  460.             e1 == "drop" or
  461.             e1 == "scroll" or
  462.             e1 == "double_touch",
  463.             application,
  464.             application.x,
  465.             application.y,
  466.             application.x + application.width - 1,
  467.             application.y + application.height - 1
  468.         )
  469.  
  470.         if application.animations then
  471.             animationIndex, animationOnFinishMethodsIndex, animationOnFinishMethods = 1, 1, {}
  472.             -- Продрачиваем анимации и вызываем обработчики кадров
  473.             while animationIndex <= #application.animations do
  474.                 animation = application.animations[animationIndex]
  475.  
  476.                 if animation.removeLater then
  477.                     table.remove(application.animations, animationIndex)
  478.  
  479.                     if #application.animations == 0 then
  480.                         application.animations = nil
  481.                         break
  482.                     end
  483.                 else
  484.                     if animation.started then
  485.                         animation.position = (computer.uptime() - animation.startUptime) / animation.duration
  486.                        
  487.                         if animation.position < 1 then
  488.                             animation.frameHandler(animation)
  489.                         else
  490.                             animation.position, animation.started = 1, false
  491.                             animation.frameHandler(animation)
  492.                            
  493.                             animationOnFinishMethods[animationOnFinishMethodsIndex] = animation
  494.                             animationOnFinishMethodsIndex = animationOnFinishMethodsIndex + 1
  495.                         end
  496.                     end
  497.  
  498.                     animationIndex = animationIndex + 1
  499.                 end
  500.             end
  501.  
  502.             -- По завершению продрочки отрисовываем изменения на экране
  503.             application:draw()
  504.  
  505.             -- Вызываем поочередно все методы .onFinish
  506.             for i = 1, #animationOnFinishMethods do
  507.                 animationOnFinishMethods[i].onFinish(animationOnFinishMethods[i])
  508.             end
  509.         end
  510.     until application.needClose
  511.  
  512.     application.needClose = nil
  513. end
  514.  
  515. local function applicationStop(application)
  516.     application.needClose = true
  517. end
  518.  
  519. local function applicationConsumeEvent(application)
  520.     application.needConsume = true
  521. end
  522.  
  523. local function applicationDraw(object, ...)
  524.     containerDraw(object)
  525.     buffer.drawChanges(...)
  526. end
  527.  
  528. function GUI.application(x, y, width, height)
  529.     local application = GUI.container(x or 1, y or 1, width or buffer.getWidth(), height or buffer.getHeight())
  530.    
  531.     application.draw = applicationDraw
  532.     application.start = applicationStart
  533.     application.stop = applicationStop
  534.     application.consumeEvent = applicationConsumeEvent
  535.  
  536.     return application
  537. end
  538.  
  539. --------------------------------------------------------------------------------
  540.  
  541. local function pressableDraw(pressable)
  542.     local background = pressable.pressed and pressable.colors.pressed.background or pressable.disabled and pressable.colors.disabled.background or pressable.colors.default.background
  543.     local text = pressable.pressed and pressable.colors.pressed.text or pressable.disabled and pressable.colors.disabled.text or pressable.colors.default.text
  544.  
  545.     if background then
  546.         buffer.drawRectangle(pressable.x, pressable.y, pressable.width, pressable.height, background, text, " ")
  547.     end
  548.     buffer.drawText(math.floor(pressable.x + pressable.width / 2 - unicode.len(pressable.text) / 2), math.floor(pressable.y + pressable.height / 2), text, pressable.text)
  549. end
  550.  
  551. local function pressableHandlePress(application, pressable, ...)
  552.     pressable.pressed = not pressable.pressed
  553.     application:draw()
  554.  
  555.     if not pressable.switchMode then
  556.         pressable.pressed = not pressable.pressed
  557.         os.sleep(GUI.BUTTON_PRESS_DURATION)
  558.        
  559.         application:draw()
  560.     end
  561.  
  562.     if pressable.onTouch then
  563.         pressable.onTouch(application, pressable, ...)
  564.     end
  565. end
  566.  
  567. local function pressableEventHandler(application, pressable, e1, ...)
  568.     if e1 == "touch" then
  569.         pressableHandlePress(application, pressable, e1, ...)
  570.     end
  571. end
  572.  
  573. local function pressable(x, y, width, height, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundDisabledColor, textDisabledColor, text)
  574.     local pressable = GUI.object(x, y, width, height)
  575.  
  576.     pressable.colors = {
  577.         default = {
  578.             background = backgroundColor,
  579.             text = textColor
  580.         },
  581.         pressed = {
  582.             background = backgroundPressedColor,
  583.             text = textPressedColor
  584.         },
  585.         disabled = {
  586.             background = backgroundDisabledColor,
  587.             text = textDisabledColor
  588.         }
  589.     }
  590.  
  591.     pressable.pressed = false
  592.     pressable.text = text
  593.     pressable.draw = pressableDraw
  594.     pressable.eventHandler = pressableEventHandler
  595.  
  596.     return pressable
  597. end
  598.  
  599. --------------------------------------------------------------------------------
  600.  
  601. local function buttonPlayAnimation(button, onFinish)
  602.     button.animationStarted = true
  603.     button:addAnimation(
  604.         function(animation)
  605.             if button.pressed then
  606.                 if button.colors.default.background and button.colors.pressed.background then
  607.                     button.animationCurrentBackground = color.transition(button.colors.pressed.background, button.colors.default.background, animation.position)
  608.                 end
  609.                 button.animationCurrentText = color.transition(button.colors.pressed.text, button.colors.default.text, animation.position)
  610.             else
  611.                 if button.colors.default.background and button.colors.pressed.background then
  612.                     button.animationCurrentBackground = color.transition(button.colors.default.background, button.colors.pressed.background, animation.position)
  613.                 end
  614.                 button.animationCurrentText = color.transition(button.colors.default.text, button.colors.pressed.text, animation.position)
  615.             end
  616.         end,
  617.         function(animation)
  618.             button.animationStarted = false
  619.             button.pressed = not button.pressed
  620.             onFinish(animation)
  621.         end
  622.     ):start(button.animationDuration)
  623. end
  624.  
  625. local function buttonPress(button, application, object, ...)
  626.     if button.animated then
  627.         local eventData = {...}
  628.        
  629.         buttonPlayAnimation(button, function(animation)
  630.             if button.onTouch then
  631.                 button.onTouch(application, button, table.unpack(eventData))
  632.             end
  633.  
  634.             animation:remove()
  635.  
  636.             if not button.switchMode then
  637.                 buttonPlayAnimation(button, function(animation)
  638.                     animation:remove()
  639.                 end)
  640.             end
  641.         end)
  642.     else
  643.         pressableHandlePress(application, button, ...)
  644.     end
  645. end
  646.  
  647. local function buttonEventHandler(application, button, e1, ...)
  648.     if e1 == "touch" and (not button.animated or not button.animationStarted) then
  649.         button:press(application, button, e1, ...)
  650.     end
  651. end
  652.  
  653. local function buttonGetColors(button)
  654.     if button.disabled then
  655.         return button.colors.disabled.background, button.colors.disabled.text
  656.     else
  657.         if button.animated and button.animationStarted then
  658.             return button.animationCurrentBackground, button.animationCurrentText
  659.         else
  660.             if button.pressed then
  661.                 return button.colors.pressed.background, button.colors.pressed.text
  662.             else
  663.                 return button.colors.default.background, button.colors.default.text
  664.             end
  665.         end
  666.     end
  667. end
  668.  
  669. local function buttonDrawText(button, textColor)
  670.     buffer.drawText(math.floor(button.x + button.width / 2 - unicode.len(button.text) / 2), math.floor(button.y + button.height / 2), textColor, button.text)
  671. end
  672.  
  673. local function buttonDraw(button)
  674.     local backgroundColor, textColor = buttonGetColors(button)
  675.    
  676.     if backgroundColor then
  677.         buffer.drawRectangle(button.x, button.y, button.width, button.height, backgroundColor, textColor, " ", button.colors.transparency)
  678.     end
  679.     buttonDrawText(button, textColor)
  680. end
  681.  
  682. local function framedButtonDraw(button)
  683.     local backgroundColor, textColor = buttonGetColors(button)
  684.    
  685.     if backgroundColor then
  686.         buffer.drawFrame(button.x, button.y, button.width, button.height, backgroundColor)
  687.     end
  688.     buttonDrawText(button, textColor)
  689. end
  690.  
  691. local function roundedButtonDraw(button)
  692.     local backgroundColor, textColor = buttonGetColors(button)
  693.  
  694.     if backgroundColor then
  695.         local x2, y2 = button.x + button.width - 1, button.y + button.height - 1
  696.         if button.height > 1 then
  697.             buffer.drawText(button.x + 1, button.y, backgroundColor, string.rep("▄", button.width - 2))
  698.             buffer.drawText(button.x, button.y, backgroundColor, "⣠")
  699.             buffer.drawText(x2, button.y, backgroundColor, "⣄")
  700.            
  701.             buffer.drawRectangle(button.x, button.y + 1, button.width, button.height - 2, backgroundColor, textColor, " ")
  702.            
  703.             buffer.drawText(button.x + 1, y2, backgroundColor, string.rep("▀", button.width - 2))
  704.             buffer.drawText(button.x, y2, backgroundColor, "⠙")
  705.             buffer.drawText(x2, y2, backgroundColor, "⠋")
  706.         else
  707.             buffer.drawRectangle(button.x, button.y, button.width, button.height, backgroundColor, textColor, " ")
  708.             GUI.roundedCorners(button.x, button.y, button.width, button.height, backgroundColor)
  709.         end
  710.     end
  711.  
  712.     buttonDrawText(button, textColor)
  713. end
  714.  
  715. local function tagButtonDraw(button)
  716.     local backgroundColor, textColor = buttonGetColors(button)
  717.    
  718.     buffer.drawRectangle(button.x, button.y, button.width, button.height, backgroundColor, textColor, " ")
  719.     buffer.drawText(button.x - 1, button.y, backgroundColor, "◀")
  720.     buttonDrawText(button, textColor)
  721. end
  722.  
  723. local function buttonCreate(x, y, width, height, backgroundColor, textColor, backgroundPressedColor, textPressedColor, text)
  724.     local button = pressable(x, y, width, height, backgroundColor, textColor, backgroundPressedColor, textPressedColor, 0x878787, 0xA5A5A5, text)
  725.  
  726.     button.animationDuration = GUI.BUTTON_ANIMATION_DURATION
  727.     button.animated = true
  728.  
  729.     button.animationCurrentBackground = backgroundColor
  730.     button.animationCurrentText = textColor
  731.    
  732.     button.press = buttonPress
  733.     button.eventHandler = buttonEventHandler
  734.  
  735.     return button
  736. end
  737.  
  738. local function adaptiveButtonCreate(x, y, xOffset, yOffset, backgroundColor, textColor, backgroundPressedColor, textPressedColor, text)
  739.     return buttonCreate(x, y, unicode.len(text) + xOffset * 2, yOffset * 2 + 1, backgroundColor, textColor, backgroundPressedColor, textPressedColor, text)
  740. end
  741.  
  742. function GUI.button(...)
  743.     local button = buttonCreate(...)
  744.     button.draw = buttonDraw
  745.  
  746.     return button
  747. end
  748.  
  749. function GUI.adaptiveButton(...)
  750.     local button = adaptiveButtonCreate(...)
  751.     button.draw = buttonDraw
  752.  
  753.     return button
  754. end
  755.  
  756. function GUI.framedButton(...)
  757.     local button = buttonCreate(...)
  758.     button.draw = framedButtonDraw
  759.  
  760.     return button
  761. end
  762.  
  763. function GUI.adaptiveFramedButton(...)
  764.     local button = adaptiveButtonCreate(...)
  765.     button.draw = framedButtonDraw
  766.  
  767.     return button
  768. end
  769.  
  770. function GUI.roundedButton(...)
  771.     local button = buttonCreate(...)
  772.     button.draw = roundedButtonDraw
  773.  
  774.     return button
  775. end
  776.  
  777. function GUI.adaptiveRoundedButton(...)
  778.     local button = adaptiveButtonCreate(...)
  779.     button.draw = roundedButtonDraw
  780.  
  781.     return button
  782. end
  783.  
  784. function GUI.tagButton(...)
  785.     local button = buttonCreate(...)
  786.     button.draw = tagButtonDraw
  787.  
  788.     return button
  789. end
  790.  
  791. function GUI.adaptiveTagButton(...)
  792.     local button = adaptiveButtonCreate(...)
  793.     button.draw = tagButtonDraw
  794.  
  795.     return button
  796. end
  797.  
  798. --------------------------------------------------------------------------------
  799.  
  800. local function drawPanel(object)
  801.     buffer.drawRectangle(object.x, object.y, object.width, object.height, object.colors.background, 0x0, " ", object.colors.transparency)
  802.     return object
  803. end
  804.  
  805. function GUI.panel(x, y, width, height, color, transparency)
  806.     local object = GUI.object(x, y, width, height)
  807.    
  808.     object.colors = {
  809.         background = color,
  810.         transparency = transparency
  811.     }
  812.     object.draw = drawPanel
  813.    
  814.     return object
  815. end
  816.  
  817. --------------------------------------------------------------------------------
  818.  
  819. local function drawLabel(object)
  820.     local xText, yText = GUI.getAlignmentCoordinates(
  821.         object.x,
  822.         object.y,
  823.         object.width,
  824.         object.height,
  825.         object.horizontalAlignment,
  826.         object.verticalAlignment,
  827.         unicode.len(object.text),
  828.         1
  829.     )
  830.     buffer.drawText(math.floor(xText), math.floor(yText), object.colors.text, object.text)
  831.     return object
  832. end
  833.  
  834. function GUI.label(x, y, width, height, textColor, text)
  835.     local object = GUI.object(x, y, width, height)
  836.    
  837.     object.setAlignment = GUI.setAlignment
  838.     object:setAlignment(GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
  839.     object.colors = {text = textColor}
  840.     object.text = text
  841.     object.draw = drawLabel
  842.  
  843.     return object
  844. end
  845.  
  846. --------------------------------------------------------------------------------
  847.  
  848. local function drawImage(object)
  849.     buffer.drawImage(object.x, object.y, object.image)
  850.     return object
  851. end
  852.  
  853. function GUI.image(x, y, image)
  854.     local object = GUI.object(x, y, image[1], image[2])
  855.     object.image = image
  856.     object.draw = drawImage
  857.     return object
  858. end
  859.  
  860. --------------------------------------------------------------------------------
  861.  
  862. function GUI.actionButtons(x, y, fatSymbol)
  863.     local symbol = fatSymbol and "⬤" or "●"
  864.    
  865.     local container = GUI.container(x, y, 5, 1)
  866.     container.close = container:addChild(GUI.button(1, 1, 1, 1, nil, 0xFF4940, nil, 0x992400, symbol))
  867.     container.minimize = container:addChild(GUI.button(3, 1, 1, 1, nil, 0xFFB640, nil, 0x996D00, symbol))
  868.     container.maximize = container:addChild(GUI.button(5, 1, 1, 1, nil, 0x00B640, nil, 0x006D40, symbol))
  869.  
  870.     return container
  871. end
  872.  
  873. --------------------------------------------------------------------------------
  874.  
  875. local function drawProgressBar(object)
  876.     local activeWidth = math.floor(math.min(object.value, 100) / 100 * object.width)
  877.     if object.thin then
  878.         buffer.drawText(object.x, object.y, object.colors.passive, string.rep("━", object.width))
  879.         buffer.drawText(object.x, object.y, object.colors.active, string.rep("━", activeWidth))
  880.     else
  881.         buffer.drawRectangle(object.x, object.y, object.width, object.height, object.colors.passive, 0x0, " ")
  882.         buffer.drawRectangle(object.x, object.y, activeWidth, object.height, object.colors.active, 0x0, " ")
  883.     end
  884.  
  885.     if object.showValue then
  886.         local stringValue = (object.valuePrefix or "") .. object.value .. (object.valuePostfix or "")
  887.         buffer.drawText(math.floor(object.x + object.width / 2 - unicode.len(stringValue) / 2), object.y + 1, object.colors.value, stringValue)
  888.     end
  889.  
  890.     return object
  891. end
  892.  
  893. function GUI.progressBar(x, y, width, activeColor, passiveColor, valueColor, value, thin, showValue, valuePrefix, valuePostfix)
  894.     local object = GUI.object(x, y, width, 1)
  895.    
  896.     object.value = value
  897.     object.colors = {active = activeColor, passive = passiveColor, value = valueColor}
  898.     object.thin = thin
  899.     object.draw = drawProgressBar
  900.     object.showValue = showValue
  901.     object.valuePrefix = valuePrefix
  902.     object.valuePostfix = valuePostfix
  903.    
  904.     return object
  905. end
  906.  
  907. --------------------------------------------------------------------------------
  908.  
  909. function GUI.drawShadow(x, y, width, height, transparency, thin)
  910.     if thin then
  911.         buffer.drawRectangle(x + width, y + 1, 1, height - 1, 0x0, 0x0, " ", transparency)
  912.         buffer.drawText(x + 1, y + height, 0x0, string.rep("▀", width), transparency)
  913.         buffer.drawText(x + width, y, 0x0, "▄", transparency)
  914.     else
  915.         buffer.drawRectangle(x + width, y + 1, 2, height, 0x0, 0x0, " ", transparency)
  916.         buffer.drawRectangle(x + 2, y + height, width - 2, 1, 0x0, 0x0, " ", transparency)
  917.     end
  918. end
  919.  
  920. function GUI.roundedCorners(x, y, width, height, color, transparency)
  921.     buffer.drawText(x - 1, y, color, "⠰", transparency)
  922.     buffer.drawText(x + width, y, color, "⠆", transparency)
  923. end
  924.  
  925. --------------------------------------------------------------------------------
  926.  
  927. function GUI.alert(...)
  928.     local args = {...}
  929.     for i = 1, #args do
  930.         if type(args[i]) == "table" then
  931.             args[i] = table.toString(args[i], true)
  932.         else
  933.             args[i] = tostring(args[i])
  934.         end
  935.     end
  936.     if #args == 0 then args[1] = "nil" end
  937.  
  938.     local sign = image.fromString([[06030000FF 0000FF 00F7FF▟00F7FF▙0000FF 0000FF 0000FF 00F7FF▟F7FF00 F7FF00 00F7FF▙0000FF 00F7FF▟F7FF00CF7FF00yF7FF00kF7FF00a00F7FF▙]])
  939.     local offset = 2
  940.     local lines = #args > 1 and "\"" .. table.concat(args, "\", \"") .. "\"" or args[1]
  941.     local bufferWidth, bufferHeight = buffer.getResolution()
  942.     local width = math.floor(bufferWidth * 0.5)
  943.     local textWidth = width - image.getWidth(sign) - 2
  944.  
  945.     lines = string.wrap(lines, textWidth)
  946.     local height = image.getHeight(sign)
  947.     if #lines + 2 > height then
  948.         height = #lines + 2
  949.     end
  950.  
  951.     local application = GUI.application(1, math.floor(bufferHeight / 2 - height / 2), bufferWidth, height + offset * 2)
  952.     local oldPixels = buffer.copy(application.x, application.y, application.width, application.height)
  953.  
  954.     local x, y = math.floor(bufferWidth / 2 - width / 2), offset + 1
  955.     application:addChild(GUI.panel(1, 1, application.width, application.height, 0x1D1D1D))
  956.     application:addChild(GUI.image(x, y, sign))
  957.     application:addChild(GUI.textBox(x + image.getWidth(sign) + 2, y, textWidth, #lines, 0x1D1D1D, 0xE1E1E1, lines, 1, 0, 0)).eventHandler = nil
  958.     local buttonWidth = 10
  959.     local button = application:addChild(GUI.roundedButton(x + image.getWidth(sign) + textWidth - buttonWidth + 2, application.height - offset, buttonWidth, 1, 0x3366CC, 0xE1E1E1, 0xE1E1E1, 0x3366CC, "OK"))
  960.    
  961.     button.onTouch = function()
  962.         application:stop()
  963.         buffer.paste(application.x, application.y, oldPixels)
  964.         buffer.drawChanges()
  965.     end
  966.  
  967.     application.eventHandler = function(application, object, e1, e2, e3, e4, ...)
  968.         if e1 == "key_down" and e4 == 28 then
  969.             button.animated = false
  970.             button:press(application, object, e1, e2, e3, e4, ...)
  971.         end
  972.     end
  973.  
  974.     application:draw(true)
  975.     application:start()
  976. end
  977.  
  978. --------------------------------------------------------------------------------
  979.  
  980. local function codeViewDraw(codeView)
  981.     local toLine, colorScheme, patterns = codeView.fromLine + codeView.height - 1, codeView.syntaxColorScheme, codeView.syntaxPatterns
  982.     -- Line numbers bar and code area
  983.     codeView.lineNumbersWidth = unicode.len(tostring(toLine)) + 2
  984.     codeView.codeAreaPosition = codeView.x + codeView.lineNumbersWidth
  985.     codeView.codeAreaWidth = codeView.width - codeView.lineNumbersWidth
  986.     -- Line numbers
  987.     buffer.drawRectangle(codeView.x, codeView.y, codeView.lineNumbersWidth, codeView.height, colorScheme.lineNumbersBackground, colorScheme.lineNumbersText, " ")  
  988.     -- Background
  989.     buffer.drawRectangle(codeView.codeAreaPosition, codeView.y, codeView.codeAreaWidth, codeView.height, colorScheme.background, colorScheme.text, " ")
  990.     -- Line numbers texts
  991.     local y = codeView.y
  992.     for line = codeView.fromLine, toLine do
  993.         if codeView.lines[line] then
  994.             local text = tostring(line)
  995.             if codeView.highlights[line] then
  996.                 buffer.drawRectangle(codeView.x, y, codeView.lineNumbersWidth, 1, codeView.highlights[line], colorScheme.text, " ", 0.3)
  997.                 buffer.drawRectangle(codeView.codeAreaPosition, y, codeView.codeAreaWidth, 1, codeView.highlights[line], colorScheme.text, " ")
  998.             end
  999.             buffer.drawText(codeView.codeAreaPosition - unicode.len(text) - 1, y, colorScheme.lineNumbersText, text)
  1000.             y = y + 1
  1001.         else
  1002.             break
  1003.         end
  1004.     end
  1005.  
  1006.     local function drawUpperSelection(y, selectionIndex)
  1007.         buffer.drawRectangle(
  1008.             codeView.codeAreaPosition + codeView.selections[selectionIndex].from.symbol - codeView.fromSymbol + 1,
  1009.             y + codeView.selections[selectionIndex].from.line - codeView.fromLine,
  1010.             codeView.codeAreaWidth - codeView.selections[selectionIndex].from.symbol + codeView.fromSymbol - 1,
  1011.             1,
  1012.             codeView.selections[selectionIndex].color or colorScheme.selection, colorScheme.text, " "
  1013.         )
  1014.     end
  1015.  
  1016.     local function drawLowerSelection(y, selectionIndex)
  1017.         buffer.drawRectangle(
  1018.             codeView.codeAreaPosition,
  1019.             y + codeView.selections[selectionIndex].from.line - codeView.fromLine,
  1020.             codeView.selections[selectionIndex].to.symbol - codeView.fromSymbol + 2,
  1021.             1,
  1022.             codeView.selections[selectionIndex].color or colorScheme.selection, colorScheme.text, " "
  1023.         )
  1024.     end
  1025.  
  1026.     local oldDrawLimitX1, oldDrawLimitY1, oldDrawLimitX2, oldDrawLimitY2 = buffer.getDrawLimit()
  1027.     buffer.setDrawLimit(codeView.codeAreaPosition, codeView.y, codeView.codeAreaPosition + codeView.codeAreaWidth - 1, codeView.y + codeView.height - 1)
  1028.  
  1029.     if #codeView.selections > 0 then
  1030.         for selectionIndex = 1, #codeView.selections do
  1031.             y = codeView.y
  1032.             local dy = codeView.selections[selectionIndex].to.line - codeView.selections[selectionIndex].from.line
  1033.             if dy == 0 then
  1034.                 buffer.drawRectangle(
  1035.                     codeView.codeAreaPosition + codeView.selections[selectionIndex].from.symbol - codeView.fromSymbol + 1,
  1036.                     y + codeView.selections[selectionIndex].from.line - codeView.fromLine,
  1037.                     codeView.selections[selectionIndex].to.symbol - codeView.selections[selectionIndex].from.symbol + 1,
  1038.                     1,
  1039.                     codeView.selections[selectionIndex].color or colorScheme.selection, colorScheme.text, " "
  1040.                 )
  1041.             elseif dy == 1 then
  1042.                 drawUpperSelection(y, selectionIndex); y = y + 1
  1043.                 drawLowerSelection(y, selectionIndex)
  1044.             else
  1045.                 drawUpperSelection(y, selectionIndex); y = y + 1
  1046.                 for i = 1, dy - 1 do
  1047.                     buffer.drawRectangle(codeView.codeAreaPosition, y + codeView.selections[selectionIndex].from.line - codeView.fromLine, codeView.codeAreaWidth, 1, codeView.selections[selectionIndex].color or colorScheme.selection, colorScheme.text, " "); y = y + 1
  1048.                 end
  1049.  
  1050.                 drawLowerSelection(y, selectionIndex)
  1051.             end
  1052.         end
  1053.     end
  1054.  
  1055.     -- Code strings
  1056.     y = codeView.y
  1057.     buffer.setDrawLimit(codeView.codeAreaPosition + 1, y, codeView.codeAreaPosition + codeView.codeAreaWidth - 2, y + codeView.height - 1)
  1058.    
  1059.     for i = codeView.fromLine, toLine do
  1060.         if codeView.lines[i] then
  1061.             if codeView.syntaxHighlight then
  1062.                 GUI.highlightString(codeView.codeAreaPosition + 1,
  1063.                     y,
  1064.                     codeView.codeAreaWidth - 2,
  1065.                     codeView.fromSymbol,
  1066.                     codeView.indentationWidth,
  1067.                     patterns,
  1068.                     colorScheme,
  1069.                     codeView.lines[i]
  1070.                 )
  1071.             else
  1072.                 buffer.drawText(codeView.codeAreaPosition - codeView.fromSymbol + 2, y, colorScheme.text, codeView.lines[i])
  1073.             end
  1074.  
  1075.             y = y + 1
  1076.         else
  1077.             break
  1078.         end
  1079.     end
  1080.  
  1081.     buffer.setDrawLimit(oldDrawLimitX1, oldDrawLimitY1, oldDrawLimitX2, oldDrawLimitY2)
  1082.  
  1083.     if #codeView.lines > codeView.height then
  1084.         codeView.verticalScrollBar.colors.background, codeView.verticalScrollBar.colors.foreground = colorScheme.scrollBarBackground, colorScheme.scrollBarForeground
  1085.         codeView.verticalScrollBar.minimumValue, codeView.verticalScrollBar.maximumValue, codeView.verticalScrollBar.value, codeView.verticalScrollBar.shownValueCount = 1, #codeView.lines, codeView.fromLine, codeView.height
  1086.         codeView.verticalScrollBar.localX = codeView.width
  1087.         codeView.verticalScrollBar.localY = 1
  1088.         codeView.verticalScrollBar.height = codeView.height - 1
  1089.         codeView.verticalScrollBar.hidden = false
  1090.     else
  1091.         codeView.verticalScrollBar.hidden = true
  1092.     end
  1093.  
  1094.     if codeView.maximumLineLength > codeView.codeAreaWidth - 2 then
  1095.         codeView.horizontalScrollBar.colors.background, codeView.horizontalScrollBar.colors.foreground = colorScheme.scrollBarBackground, colorScheme.scrollBarForeground
  1096.         codeView.horizontalScrollBar.minimumValue, codeView.horizontalScrollBar.maximumValue, codeView.horizontalScrollBar.value, codeView.horizontalScrollBar.shownValueCount = 1, codeView.maximumLineLength, codeView.fromSymbol, codeView.codeAreaWidth - 2
  1097.         codeView.horizontalScrollBar.localX = codeView.lineNumbersWidth + 1
  1098.         codeView.horizontalScrollBar.localY = codeView.height
  1099.         codeView.horizontalScrollBar.width = codeView.codeAreaWidth - 1
  1100.         codeView.horizontalScrollBar.hidden = false
  1101.     else
  1102.         codeView.horizontalScrollBar.hidden = true
  1103.     end
  1104.  
  1105.     codeView:overrideDraw()
  1106. end
  1107.  
  1108. function GUI.codeView(x, y, width, height, fromSymbol, fromLine, maximumLineLength, selections, highlights, syntaxPatterns, syntaxColorScheme, syntaxHighlight, lines) 
  1109.     local codeView = GUI.container(x, y, width, height)
  1110.    
  1111.     codeView.passScreenEvents = false
  1112.     codeView.lines = lines
  1113.     codeView.fromSymbol = fromSymbol
  1114.     codeView.fromLine = fromLine
  1115.     codeView.maximumLineLength = maximumLineLength
  1116.     codeView.selections = selections or {}
  1117.     codeView.highlights = highlights or {}
  1118.     codeView.syntaxHighlight = syntaxHighlight
  1119.     codeView.syntaxPatterns = syntaxPatterns
  1120.     codeView.syntaxColorScheme = syntaxColorScheme
  1121.     codeView.indentationWidth = 2
  1122.  
  1123.     codeView.verticalScrollBar = codeView:addChild(GUI.scrollBar(1, 1, 1, 1, 0x0, 0x0, 1, 1, 1, 1, 1, true))
  1124.     codeView.horizontalScrollBar = codeView:addChild(GUI.scrollBar(1, 1, 1, 1, 0x0, 0x0, 1, 1, 1, 1, 1, true))
  1125.  
  1126.     codeView.overrideDraw = codeView.draw
  1127.     codeView.draw = codeViewDraw
  1128.  
  1129.     return codeView
  1130. end
  1131.  
  1132. --------------------------------------------------------------------------------
  1133.  
  1134. local function colorSelectorDraw(colorSelector)
  1135.     local overlayColor = colorSelector.color < 0x7FFFFF and 0xFFFFFF or 0x0
  1136.        
  1137.     buffer.drawRectangle(
  1138.         colorSelector.x,
  1139.         colorSelector.y,
  1140.         colorSelector.width,
  1141.         colorSelector.height,
  1142.         colorSelector.pressed and color.blend(colorSelector.color, overlayColor, 0.8) or colorSelector.color,
  1143.         overlayColor,
  1144.         " "
  1145.     )
  1146.    
  1147.     if colorSelector.height > 1 and colorSelector.drawLine then
  1148.         buffer.drawText(colorSelector.x, colorSelector.y + colorSelector.height - 1, overlayColor, string.rep("▄", colorSelector.width), 0.8)
  1149.     end
  1150.    
  1151.     buffer.drawText(colorSelector.x + 1, colorSelector.y + math.floor(colorSelector.height / 2), overlayColor, string.limit(colorSelector.text, colorSelector.width - 2))
  1152.    
  1153.     return colorSelector
  1154. end
  1155.  
  1156. local function colorSelectorEventHandler(application, object, e1, ...)
  1157.     if e1 == "touch" then
  1158.         local eventData = {...}
  1159.         object.pressed = true
  1160.  
  1161.         local palette = application:addChild(GUI.palette(1, 1, object.color))
  1162.         palette.localX, palette.localY = math.floor(application.width / 2 - palette.width / 2), math.floor(application.height / 2 - palette.height / 2)
  1163.  
  1164.         palette.cancelButton.onTouch = function()
  1165.             object.pressed = false
  1166.             palette:remove()
  1167.             application:draw()
  1168.  
  1169.             if object.onColorSelected then
  1170.                 object.onColorSelected(application, object, e1, table.unpack(eventData))
  1171.             end
  1172.         end
  1173.  
  1174.         palette.submitButton.onTouch = function()
  1175.             object.color = palette.color.integer
  1176.             palette.cancelButton.onTouch()
  1177.         end
  1178.        
  1179.         application:draw()
  1180.     end
  1181. end
  1182.  
  1183. function GUI.colorSelector(x, y, width, height, color, text)
  1184.     local colorSelector = GUI.object(x, y, width, height)
  1185.    
  1186.     colorSelector.drawLine = true
  1187.     colorSelector.eventHandler = colorSelectorEventHandler
  1188.     colorSelector.color = color
  1189.     colorSelector.text = text
  1190.     colorSelector.draw = colorSelectorDraw
  1191.    
  1192.     return colorSelector
  1193. end
  1194.  
  1195. --------------------------------------------------------------------------------
  1196.  
  1197. local function getAxisValue(number, postfix, roundValues)
  1198.     if roundValues then
  1199.         return math.floor(number) .. postfix
  1200.     else
  1201.         local integer, fractional = math.modf(number)
  1202.         local firstPart, secondPart = "", ""
  1203.         if math.abs(integer) >= 1000 then
  1204.             return math.shorten(integer, 2) .. postfix
  1205.         else
  1206.             if math.abs(fractional) > 0 then
  1207.                 return string.format("%.2f", number) .. postfix
  1208.             else
  1209.                 return number .. postfix
  1210.             end
  1211.         end
  1212.     end
  1213. end
  1214.  
  1215. local function drawChart(object)
  1216.     -- Sorting by x value
  1217.     local valuesCopy = {}
  1218.     for i = 1, #object.values do valuesCopy[i] = object.values[i] end
  1219.     table.sort(valuesCopy, function(a, b) return a[1] < b[1] end)
  1220.    
  1221.     if #valuesCopy == 0 then valuesCopy = {{0, 0}} end
  1222.  
  1223.     -- Max, min, deltas
  1224.     local xMin, xMax, yMin, yMax = valuesCopy[1][1], valuesCopy[#valuesCopy][1], valuesCopy[1][2], valuesCopy[1][2]
  1225.     for i = 1, #valuesCopy do yMin, yMax = math.min(yMin, valuesCopy[i][2]), math.max(yMax, valuesCopy[i][2]) end
  1226.     local dx, dy = xMax - xMin, yMax - yMin
  1227.  
  1228.     -- y axis values and helpers
  1229.     local value, chartHeight, yAxisValueMaxWidth, yAxisValues = yMin, object.height - 1 - (object.showXAxisValues and 1 or 0), 0, {}
  1230.     for y = object.y + object.height - 3, object.y + 1, -chartHeight * object.yAxisValueInterval do
  1231.         local stringValue = getAxisValue(value, object.yAxisPostfix, object.roundValues)
  1232.         yAxisValueMaxWidth = math.max(yAxisValueMaxWidth, unicode.len(stringValue))
  1233.         table.insert(yAxisValues, {y = math.ceil(y), value = stringValue})
  1234.         value = value + dy * object.yAxisValueInterval
  1235.     end
  1236.     local stringValue = getAxisValue(yMax, object.yAxisPostfix, object.roundValues)
  1237.     table.insert(yAxisValues, {y = object.y, value = stringValue})
  1238.     yAxisValueMaxWidth = math.max(yAxisValueMaxWidth, unicode.len(stringValue))
  1239.  
  1240.     local chartWidth = object.width - (object.showYAxisValues and yAxisValueMaxWidth + 2 or 0)
  1241.     local chartX = object.x + object.width - chartWidth
  1242.     for i = 1, #yAxisValues do
  1243.         if object.showYAxisValues then
  1244.             buffer.drawText(chartX - unicode.len(yAxisValues[i].value) - 2, yAxisValues[i].y, object.colors.axisValue, yAxisValues[i].value)
  1245.         end
  1246.         buffer.drawText(chartX, yAxisValues[i].y, object.colors.helpers, string.rep("─", chartWidth))
  1247.     end
  1248.  
  1249.     -- x axis values
  1250.     if object.showXAxisValues then
  1251.         value = xMin
  1252.         for x = chartX, chartX + chartWidth - 2, chartWidth * object.xAxisValueInterval do
  1253.             local stringValue = getAxisValue(value, object.xAxisPostfix, object.roundValues)
  1254.             buffer.drawText(math.floor(x - unicode.len(stringValue) / 2), object.y + object.height - 1, object.colors.axisValue, stringValue)
  1255.             value = value + dx * object.xAxisValueInterval
  1256.         end
  1257.         local value = getAxisValue(xMax, object.xAxisPostfix, object.roundValues)
  1258.         buffer.drawText(object.x + object.width - unicode.len(value), object.y + object.height - 1, object.colors.axisValue, value)
  1259.     end
  1260.  
  1261.     -- Axis lines
  1262.     for y = object.y, object.y + chartHeight - 1 do
  1263.         buffer.drawText(chartX - 1, y, object.colors.axis, "┨")
  1264.     end
  1265.     buffer.drawText(chartX - 1, object.y + chartHeight, object.colors.axis, "┗" .. string.rep("┯━", math.floor(chartWidth / 2)))
  1266.  
  1267.     local function fillVerticalPart(x1, y1, x2, y2)
  1268.         local dx, dy = x2 - x1, y2 - y1
  1269.         local absdx, absdy = math.abs(dx), math.abs(dy)
  1270.         if absdx >= absdy then
  1271.             local step, y = dy / absdx, y1
  1272.             for x = x1, x2, (x1 < x2 and 1 or -1) do
  1273.                 local yFloor = math.floor(y)
  1274.                 buffer.drawSemiPixelRectangle(math.floor(x), yFloor, 1, math.floor(object.y + chartHeight) * 2 - yFloor - 1, object.colors.chart)
  1275.                 y = y + step
  1276.             end
  1277.         else
  1278.             local step, x = dx / absdy, x1
  1279.             for y = y1, y2, (y1 < y2 and 1 or -1) do
  1280.                 local yFloor = math.floor(y)
  1281.                 buffer.drawSemiPixelRectangle(math.floor(x), yFloor, 1, math.floor(object.y + chartHeight) * 2 - yFloor - 1, object.colors.chart)
  1282.                 x = x + step
  1283.             end
  1284.         end
  1285.     end
  1286.  
  1287.     -- chart
  1288.     for i = 1, #valuesCopy - 1 do
  1289.         local x = math.floor(chartX + (valuesCopy[i][1] - xMin) / dx * (chartWidth - 1))
  1290.         local y = math.floor(object.y + chartHeight - 1 - (valuesCopy[i][2] - yMin) / dy * (chartHeight - 1)) * 2
  1291.         local xNext = math.floor(chartX + (valuesCopy[i + 1][1] - xMin) / dx * (chartWidth - 1))
  1292.         local yNext = math.floor(object.y + chartHeight - 1 - (valuesCopy[i + 1][2] - yMin) / dy * (chartHeight - 1)) * 2
  1293.         if object.fillChartArea then
  1294.             fillVerticalPart(x, y, xNext, yNext)
  1295.         else
  1296.             buffer.drawSemiPixelLine(x, y, xNext, yNext, object.colors.chart)
  1297.         end
  1298.     end
  1299.  
  1300.     return object
  1301. end
  1302.  
  1303. function GUI.chart(x, y, width, height, axisColor, axisValueColor, axisHelpersColor, chartColor, xAxisValueInterval, yAxisValueInterval, xAxisPostfix, yAxisPostfix, fillChartArea, values)
  1304.     local object = GUI.object(x, y, width, height)
  1305.  
  1306.     object.colors = {axis = axisColor, chart = chartColor, axisValue = axisValueColor, helpers = axisHelpersColor}
  1307.     object.draw = drawChart
  1308.     object.values = values or {}
  1309.     object.xAxisPostfix = xAxisPostfix
  1310.     object.yAxisPostfix = yAxisPostfix
  1311.     object.xAxisValueInterval = xAxisValueInterval
  1312.     object.yAxisValueInterval = yAxisValueInterval
  1313.     object.fillChartArea = fillChartArea
  1314.     object.showYAxisValues = true
  1315.     object.showXAxisValues = true
  1316.  
  1317.     return object
  1318. end
  1319.  
  1320. --------------------------------------------------------------------------------
  1321.  
  1322. local function switchAndLabelDraw(switchAndLabel)
  1323.     switchAndLabel.label.width = switchAndLabel.width
  1324.     switchAndLabel.switch.localX = switchAndLabel.width - switchAndLabel.switch.width
  1325.  
  1326.     switchAndLabel.label.x, switchAndLabel.label.y = switchAndLabel.x + switchAndLabel.label.localX - 1, switchAndLabel.y + switchAndLabel.label.localY - 1
  1327.     switchAndLabel.switch.x, switchAndLabel.switch.y = switchAndLabel.x + switchAndLabel.switch.localX - 1, switchAndLabel.y + switchAndLabel.switch.localY - 1
  1328.    
  1329.     switchAndLabel.label:draw()
  1330.     switchAndLabel.switch:draw()
  1331.  
  1332.     return switchAndLabel
  1333. end
  1334.  
  1335. function GUI.switchAndLabel(x, y, width, switchWidth, activeColor, passiveColor, pipeColor, textColor, text, switchState)
  1336.     local switchAndLabel = GUI.container(x, y, width, 1)
  1337.  
  1338.     switchAndLabel.label = switchAndLabel:addChild(GUI.label(1, 1, width, 1, textColor, text))
  1339.     switchAndLabel.switch = switchAndLabel:addChild(GUI.switch(1, 1, switchWidth, activeColor, passiveColor, pipeColor, switchState))
  1340.     switchAndLabel.draw = switchAndLabelDraw
  1341.  
  1342.     return switchAndLabel
  1343. end
  1344.  
  1345. --------------------------------------------------------------------------------
  1346.  
  1347. local function sliderDraw(object)
  1348.     -- На всякий случай делаем значение не меньше минимального и не больше максимального
  1349.     object.value = math.min(math.max(object.value, object.minimumValue), object.maximumValue)
  1350.    
  1351.     if object.showMaximumAndMinimumValues then
  1352.         local stringMaximumValue, stringMinimumValue = tostring(object.roundValues and math.floor(object.maximumValue) or math.roundToDecimalPlaces(object.maximumValue, 2)), tostring(object.roundValues and math.floor(object.minimumValue) or math.roundToDecimalPlaces(object.minimumValue, 2))
  1353.         buffer.drawText(object.x - unicode.len(stringMinimumValue) - 1, object.y, object.colors.value, stringMinimumValue)
  1354.         buffer.drawText(object.x + object.width + 1, object.y, object.colors.value, stringMaximumValue)
  1355.     end
  1356.  
  1357.     if object.currentValuePrefix or object.currentValuePostfix then
  1358.         local stringCurrentValue = (object.currentValuePrefix or "") .. (object.roundValues and math.floor(object.value) or math.roundToDecimalPlaces(object.value, 2)) .. (object.currentValuePostfix or "")
  1359.         buffer.drawText(math.floor(object.x + object.width / 2 - unicode.len(stringCurrentValue) / 2), object.y + 1, object.colors.value, stringCurrentValue)
  1360.     end
  1361.  
  1362.     local activeWidth = math.round((object.value - object.minimumValue) / (object.maximumValue - object.minimumValue) * object.width)
  1363.     buffer.drawText(object.x, object.y, object.colors.passive, string.rep("━", object.width))
  1364.     buffer.drawText(object.x, object.y, object.colors.active, string.rep("━", activeWidth))
  1365.     buffer.drawText(activeWidth >= object.width and object.x + activeWidth - 1 or object.x + activeWidth, object.y, object.colors.pipe, "⬤")
  1366.  
  1367.     return object
  1368. end
  1369.  
  1370. local function sliderEventHandler(application, object, e1, e2, e3, ...)
  1371.     if e1 == "touch" or e1 == "drag" then
  1372.         local clickPosition = e3 - object.x
  1373.  
  1374.         if clickPosition == 0 then
  1375.             object.value = object.minimumValue
  1376.         elseif clickPosition == object.width - 1 then
  1377.             object.value = object.maximumValue
  1378.         else
  1379.             object.value = object.minimumValue + (clickPosition / object.width * (object.maximumValue - object.minimumValue))
  1380.         end
  1381.  
  1382.         application:draw()
  1383.  
  1384.         if object.onValueChanged then
  1385.             object.onValueChanged(application, object, e1, e2, e3, ...)
  1386.         end
  1387.     end
  1388. end
  1389.  
  1390. function GUI.slider(x, y, width, activeColor, passiveColor, pipeColor, valueColor, minimumValue, maximumValue, value, showMaximumAndMinimumValues, currentValuePrefix, currentValuePostfix)
  1391.     local object = GUI.object(x, y, width, 1)
  1392.    
  1393.     object.eventHandler = sliderEventHandler
  1394.     object.colors = {active = activeColor, passive = passiveColor, pipe = pipeColor, value = valueColor}
  1395.     object.draw = sliderDraw
  1396.     object.minimumValue = minimumValue
  1397.     object.maximumValue = maximumValue
  1398.     object.value = value
  1399.     object.showMaximumAndMinimumValues = showMaximumAndMinimumValues
  1400.     object.currentValuePrefix = currentValuePrefix
  1401.     object.currentValuePostfix = currentValuePostfix
  1402.     object.roundValues = false
  1403.    
  1404.     return object
  1405. end
  1406.  
  1407. --------------------------------------------------------------------------------
  1408.  
  1409. local function switchDraw(switch)
  1410.     buffer.drawText(switch.x - 1, switch.y, switch.colors.passive, "⠰")
  1411.     buffer.drawRectangle(switch.x, switch.y, switch.width, 1, switch.colors.passive, 0x0, " ")
  1412.     buffer.drawText(switch.x + switch.width, switch.y, switch.colors.passive, "⠆")
  1413.  
  1414.     buffer.drawText(switch.x - 1, switch.y, switch.colors.active, "⠰")
  1415.     buffer.drawRectangle(switch.x, switch.y, switch.pipePosition - 1, 1, switch.colors.active, 0x0, " ")
  1416.  
  1417.     buffer.drawText(switch.x + switch.pipePosition - 2, switch.y, switch.colors.pipe, "⠰")
  1418.     buffer.drawRectangle(switch.x + switch.pipePosition - 1, switch.y, 2, 1, switch.colors.pipe, 0x0, " ")
  1419.     buffer.drawText(switch.x + switch.pipePosition + 1, switch.y, switch.colors.pipe, "⠆")
  1420.    
  1421.     return switch
  1422. end
  1423.  
  1424. local function switchSetState(switch, state)
  1425.     switch.state = state
  1426.     switch.pipePosition = switch.state and switch.width - 1 or 1
  1427.  
  1428.     return switch
  1429. end
  1430.  
  1431. local function switchEventHandler(application, switch, e1, ...)
  1432.     if e1 == "touch" then
  1433.         local eventData = {...}
  1434.  
  1435.         switch.state = not switch.state
  1436.         switch:addAnimation(
  1437.             function(animation)
  1438.                 if switch.state then
  1439.                     switch.pipePosition = math.round(1 + animation.position * (switch.width - 2))
  1440.                 else   
  1441.                     switch.pipePosition = math.round(1 + (1 - animation.position) * (switch.width - 2))
  1442.                 end
  1443.             end,
  1444.             function(animation)
  1445.                 animation:remove()
  1446.                 if switch.onStateChanged then
  1447.                     switch.onStateChanged(switch, e1, table.unpack(eventData))
  1448.                 end
  1449.             end
  1450.         ):start(switch.animationDuration)
  1451.     end
  1452. end
  1453.  
  1454. function GUI.switch(x, y, width, activeColor, passiveColor, pipeColor, state)
  1455.     local switch = GUI.object(x, y, width, 1)
  1456.  
  1457.     switch.pipePosition = 1
  1458.     switch.eventHandler = switchEventHandler
  1459.     switch.colors = {
  1460.         active = activeColor,
  1461.         passive = passiveColor,
  1462.         pipe = pipeColor,
  1463.     }
  1464.     switch.draw = switchDraw
  1465.     switch.state = state or false
  1466.     switch.update = switchUpdate
  1467.     switch.animated = true
  1468.     switch.animationDuration = GUI.SWITCH_ANIMATION_DURATION
  1469.     switch.setState = switchSetState
  1470.  
  1471.     switch:setState(state)
  1472.    
  1473.     return switch
  1474. end
  1475.  
  1476. --------------------------------------------------------------------------------
  1477.  
  1478. local function layoutCheckCell(layout, column, row)
  1479.     if column < 1 or column > #layout.columnSizes or row < 1 or row > #layout.rowSizes then
  1480.         error("Specified grid position (" .. tostring(column) .. "x" .. tostring(row) .. ") is out of layout grid range")
  1481.     end
  1482. end
  1483.  
  1484. local function layoutGetAbsoluteTotalSize(array)
  1485.     local absoluteTotalSize = 0
  1486.     for i = 1, #array do
  1487.         if array[i].sizePolicy == GUI.SIZE_POLICY_ABSOLUTE then
  1488.             absoluteTotalSize = absoluteTotalSize + array[i].size
  1489.         end
  1490.     end
  1491.     return absoluteTotalSize
  1492. end
  1493.  
  1494. local function layoutGetCalculatedSize(array, index, dependency)
  1495.     if array[index].sizePolicy == GUI.SIZE_POLICY_RELATIVE then
  1496.         array[index].calculatedSize = array[index].size * dependency
  1497.     else
  1498.         array[index].calculatedSize = array[index].size
  1499.     end
  1500. end
  1501.  
  1502. local function layoutUpdate(layout)
  1503.     local columnPercentageTotalSize, rowPercentageTotalSize = layout.width - layoutGetAbsoluteTotalSize(layout.columnSizes), layout.height - layoutGetAbsoluteTotalSize(layout.rowSizes)
  1504.     for row = 1, #layout.rowSizes do
  1505.         layoutGetCalculatedSize(layout.rowSizes, row, rowPercentageTotalSize)
  1506.         for column = 1, #layout.columnSizes do
  1507.             layoutGetCalculatedSize(layout.columnSizes, column, columnPercentageTotalSize)
  1508.             layout.cells[row][column].childrenWidth, layout.cells[row][column].childrenHeight = 0, 0
  1509.         end
  1510.     end
  1511.  
  1512.     -- Подготавливаем объекты к расположению и подсчитываем тотальные размеры
  1513.     local child, layoutRow, layoutColumn, cell
  1514.     for i = 1, #layout.children do
  1515.         child = layout.children[i]
  1516.        
  1517.         if not child.hidden then
  1518.             layoutRow, layoutColumn = child.layoutRow, child.layoutColumn
  1519.  
  1520.             -- Проверка на позицию в сетке
  1521.             if layoutRow >= 1 and layoutRow <= #layout.rowSizes and layoutColumn >= 1 and layoutColumn <= #layout.columnSizes then
  1522.                 cell = layout.cells[layoutRow][layoutColumn]
  1523.                 -- Авто-фиттинг объектов
  1524.                 if cell.horizontalFitting then
  1525.                     child.width = math.round(layout.columnSizes[layoutColumn].calculatedSize - cell.horizontalFittingRemove)
  1526.                 end
  1527.  
  1528.                 if cell.verticalFitting then
  1529.                     child.height = math.round(layout.rowSizes[layoutRow].calculatedSize - cell.verticalFittingRemove)
  1530.                 end
  1531.  
  1532.                 -- Направление и расчет размеров
  1533.                 if cell.direction == GUI.DIRECTION_HORIZONTAL then
  1534.                     cell.childrenWidth = cell.childrenWidth + child.width + cell.spacing
  1535.                     cell.childrenHeight = math.max(cell.childrenHeight, child.height)
  1536.                 else
  1537.                     cell.childrenWidth = math.max(cell.childrenWidth, child.width)
  1538.                     cell.childrenHeight = cell.childrenHeight + child.height + cell.spacing
  1539.                 end
  1540.             else
  1541.                 error("Layout child with index " .. i .. " has been assigned to cell (" .. layoutColumn .. "x" .. layoutRow .. ") out of layout grid range")
  1542.             end
  1543.         end
  1544.     end
  1545.  
  1546.     -- Высчитываем стартовую позицию объектов ячейки
  1547.     local x, y = 1, 1
  1548.     for row = 1, #layout.rowSizes do
  1549.         for column = 1, #layout.columnSizes do
  1550.             cell = layout.cells[row][column]
  1551.             cell.x, cell.y = GUI.getAlignmentCoordinates(
  1552.                 x,
  1553.                 y,
  1554.                 layout.columnSizes[column].calculatedSize,
  1555.                 layout.rowSizes[row].calculatedSize,
  1556.                 cell.horizontalAlignment,
  1557.                 cell.verticalAlignment,
  1558.                 cell.childrenWidth - (cell.direction == GUI.DIRECTION_HORIZONTAL and cell.spacing or 0),
  1559.                 cell.childrenHeight - (cell.direction == GUI.DIRECTION_VERTICAL and cell.spacing or 0)
  1560.             )
  1561.  
  1562.             -- Учитываем отступы от краев ячейки
  1563.             if cell.horizontalMargin ~= 0 or cell.verticalMargin ~= 0 then
  1564.                 cell.x, cell.y = GUI.getMarginCoordinates(
  1565.                     cell.x,
  1566.                     cell.y,
  1567.                     cell.horizontalAlignment,
  1568.                     cell.verticalAlignment,
  1569.                     cell.horizontalMargin,
  1570.                     cell.verticalMargin
  1571.                 )
  1572.             end
  1573.  
  1574.             x = x + layout.columnSizes[column].calculatedSize
  1575.         end
  1576.  
  1577.         x, y = 1, y + layout.rowSizes[row].calculatedSize
  1578.     end
  1579.  
  1580.     -- Размещаем все объекты
  1581.     for i = 1, #layout.children do
  1582.         child = layout.children[i]
  1583.        
  1584.         if not child.hidden then
  1585.             cell = layout.cells[child.layoutRow][child.layoutColumn]
  1586.            
  1587.             child.localX, cell.localY = GUI.getAlignmentCoordinates(
  1588.                 cell.x,
  1589.                 cell.y,
  1590.                 cell.childrenWidth,
  1591.                 cell.childrenHeight,
  1592.                 cell.horizontalAlignment,
  1593.                 cell.verticalAlignment,
  1594.                 child.width,
  1595.                 child.height
  1596.             )
  1597.  
  1598.             if cell.direction == GUI.DIRECTION_HORIZONTAL then
  1599.                 child.localX, child.localY = math.floor(cell.x), math.floor(cell.localY)
  1600.                 cell.x = cell.x + child.width + cell.spacing
  1601.             else
  1602.                 child.localX, child.localY = math.floor(child.localX), math.floor(cell.y)
  1603.                 cell.y = cell.y + child.height + cell.spacing
  1604.             end
  1605.         end
  1606.     end
  1607. end
  1608.  
  1609. local function layoutSetPosition(layout, column, row, object)
  1610.     layoutCheckCell(layout, column, row)
  1611.     object.layoutRow = row
  1612.     object.layoutColumn = column
  1613.  
  1614.     return object
  1615. end
  1616.  
  1617. local function layoutSetDirection(layout, column, row, direction)
  1618.     layoutCheckCell(layout, column, row)
  1619.     layout.cells[row][column].direction = direction
  1620.  
  1621.     return layout
  1622. end
  1623.  
  1624. local function layoutSetSpacing(layout, column, row, spacing)
  1625.     layoutCheckCell(layout, column, row)
  1626.     layout.cells[row][column].spacing = spacing
  1627.  
  1628.     return layout
  1629. end
  1630.  
  1631. local function layoutSetAlignment(layout, column, row, horizontalAlignment, verticalAlignment)
  1632.     layoutCheckCell(layout, column, row)
  1633.     layout.cells[row][column].horizontalAlignment, layout.cells[row][column].verticalAlignment = horizontalAlignment, verticalAlignment
  1634.  
  1635.     return layout
  1636. end
  1637.  
  1638. local function layoutGetMargin(layout, column, row)
  1639.     layoutCheckCell(layout, column, row)
  1640.  
  1641.     return layout.cells[row][column].horizontalMargin, layout.cells[row][column].verticalMargin
  1642. end
  1643.  
  1644. local function layoutSetMargin(layout, column, row, horizontalMargin, verticalMargin)
  1645.     layoutCheckCell(layout, column, row)
  1646.     layout.cells[row][column].horizontalMargin = horizontalMargin
  1647.     layout.cells[row][column].verticalMargin = verticalMargin
  1648.  
  1649.     return layout
  1650. end
  1651.  
  1652. local function layoutNewCell()
  1653.     return {
  1654.         horizontalAlignment = GUI.ALIGNMENT_HORIZONTAL_CENTER,
  1655.         verticalAlignment = GUI.ALIGNMENT_VERTICAL_CENTER,
  1656.         horizontalMargin = 0,
  1657.         verticalMargin = 0,
  1658.         direction = GUI.DIRECTION_VERTICAL,
  1659.         spacing = 1
  1660.     }
  1661. end
  1662.  
  1663. local function layoutCalculatePercentageSize(changingExistent, array, index)
  1664.     if array[index].sizePolicy == GUI.SIZE_POLICY_RELATIVE then
  1665.         local allPercents, beforeFromIndexPercents = 0, 0
  1666.         for i = 1, #array do
  1667.             if array[i].sizePolicy == GUI.SIZE_POLICY_RELATIVE then
  1668.                 allPercents = allPercents + array[i].size
  1669.  
  1670.                 if i <= index then
  1671.                     beforeFromIndexPercents = beforeFromIndexPercents + array[i].size
  1672.                 end
  1673.             end
  1674.         end
  1675.  
  1676.         local modifyer
  1677.         if changingExistent then
  1678.             if beforeFromIndexPercents > 1 then
  1679.                 error("Layout summary percentage > 100% at index " .. index)
  1680.             end
  1681.             modifyer = (1 - beforeFromIndexPercents) / (allPercents - beforeFromIndexPercents)
  1682.         else
  1683.             modifyer = (1 - array[index].size) / (allPercents - array[index].size)
  1684.         end
  1685.  
  1686.         for i = changingExistent and index + 1 or 1, #array do
  1687.             if array[i].sizePolicy == GUI.SIZE_POLICY_RELATIVE and i ~= index then
  1688.                 array[i].size = modifyer * array[i].size
  1689.             end
  1690.         end
  1691.     end
  1692. end
  1693.  
  1694. local function layoutSetColumnWidth(layout, column, sizePolicy, size)
  1695.     layout.columnSizes[column].sizePolicy, layout.columnSizes[column].size = sizePolicy, size
  1696.     layoutCalculatePercentageSize(true, layout.columnSizes, column)
  1697.  
  1698.     return layout
  1699. end
  1700.  
  1701. local function layoutSetRowHeight(layout, row, sizePolicy, size)
  1702.     layout.rowSizes[row].sizePolicy, layout.rowSizes[row].size = sizePolicy, size
  1703.     layoutCalculatePercentageSize(true, layout.rowSizes, row)
  1704.  
  1705.     return layout
  1706. end
  1707.  
  1708. local function layoutAddColumn(layout, sizePolicy, size)
  1709.     for i = 1, #layout.rowSizes do
  1710.         table.insert(layout.cells[i], layoutNewCell())
  1711.     end
  1712.  
  1713.     table.insert(layout.columnSizes, {
  1714.         sizePolicy = sizePolicy,
  1715.         size = size
  1716.     })
  1717.     layoutCalculatePercentageSize(false, layout.columnSizes, #layout.columnSizes)
  1718.  
  1719.     return layout
  1720. end
  1721.  
  1722. local function layoutAddRow(layout, sizePolicy, size)
  1723.     local row = {}
  1724.     for i = 1, #layout.columnSizes do
  1725.         table.insert(row, layoutNewCell())
  1726.     end
  1727.  
  1728.     table.insert(layout.cells, row)
  1729.     table.insert(layout.rowSizes, {
  1730.         sizePolicy = sizePolicy,
  1731.         size = size
  1732.     })
  1733.  
  1734.     layoutCalculatePercentageSize(false, layout.rowSizes, #layout.rowSizes)
  1735.  
  1736.     return layout
  1737. end
  1738.  
  1739. local function layoutRemoveRow(layout, row)
  1740.     table.remove(layout.cells, row)
  1741.  
  1742.     layout.rowSizes[row].size = 0
  1743.     layoutCalculatePercentageSize(false, layout.rowSizes, row)
  1744.  
  1745.     table.remove(layout.rowSizes, row)
  1746.  
  1747.     return layout
  1748. end
  1749.  
  1750. local function layoutRemoveColumn(layout, column)
  1751.     for i = 1, #layout.rowSizes do
  1752.         table.remove(layout.cells[i], column)
  1753.     end
  1754.  
  1755.     layout.columnSizes[column].size = 0
  1756.     layoutCalculatePercentageSize(false, layout.columnSizes, column)
  1757.  
  1758.     table.remove(layout.columnSizes, column)
  1759.  
  1760.     return layout
  1761. end
  1762.  
  1763. local function layoutSetGridSize(layout, columnCount, rowCount)
  1764.     layout.cells = {}
  1765.     layout.rowSizes = {}
  1766.     layout.columnSizes = {}
  1767.  
  1768.     local rowSize, columnSize = 1 / rowCount, 1 / columnCount
  1769.     for i = 1, rowCount do
  1770.         layoutAddRow(layout, GUI.SIZE_POLICY_RELATIVE, 1 / i)
  1771.     end
  1772.  
  1773.     for i = 1, columnCount do
  1774.         layoutAddColumn(layout, GUI.SIZE_POLICY_RELATIVE, 1 / i)
  1775.     end
  1776.  
  1777.     return layout
  1778. end
  1779.  
  1780. local function layoutDraw(layout)
  1781.     layout:update()
  1782.     containerDraw(layout)
  1783.    
  1784.     if layout.showGrid then
  1785.         local x, y = layout.x, layout.y
  1786.         for j = 1, #layout.columnSizes do
  1787.             for i = 1, #layout.rowSizes do
  1788.                 buffer.drawFrame(
  1789.                     math.round(x),
  1790.                     math.round(y),
  1791.                     math.round(layout.columnSizes[j].calculatedSize),
  1792.                     math.round(layout.rowSizes[i].calculatedSize),
  1793.                     0xFF0000
  1794.                 )
  1795.                 y = y + layout.rowSizes[i].calculatedSize
  1796.             end
  1797.             x, y = x + layout.columnSizes[j].calculatedSize, layout.y
  1798.         end
  1799.     end
  1800. end
  1801.  
  1802. local function layoutFitToChildrenSize(layout, column, row)
  1803.     layout.width, layout.height = 0, 0
  1804.  
  1805.     for i = 1, #layout.children do
  1806.         if not layout.children[i].hidden then
  1807.             if layout.cells[row][column].direction == GUI.DIRECTION_HORIZONTAL then
  1808.                 layout.width = layout.width + layout.children[i].width + layout.cells[row][column].spacing
  1809.                 layout.height = math.max(layout.height, layout.children[i].height)
  1810.             else
  1811.                 layout.width = math.max(layout.width, layout.children[i].width)
  1812.                 layout.height = layout.height + layout.children[i].height + layout.cells[row][column].spacing
  1813.             end
  1814.         end
  1815.     end
  1816.  
  1817.     if layout.cells[row][column].direction == GUI.DIRECTION_HORIZONTAL then
  1818.         layout.width = layout.width - layout.cells[row][column].spacing
  1819.     else
  1820.         layout.height = layout.height - layout.cells[row][column].spacing
  1821.     end
  1822.  
  1823.     return layout
  1824. end
  1825.  
  1826. local function layoutSetFitting(layout, column, row, horizontal, vertical, horizontalRemove, verticalRemove)
  1827.     layoutCheckCell(layout, column, row)
  1828.     layout.cells[row][column].horizontalFitting = horizontal
  1829.     layout.cells[row][column].verticalFitting = vertical
  1830.     layout.cells[row][column].horizontalFittingRemove = horizontalRemove or 0
  1831.     layout.cells[row][column].verticalFittingRemove = verticalRemove or 0
  1832.  
  1833.     return layout
  1834. end
  1835.  
  1836. local function layoutAddChild(layout, object, ...)
  1837.     object.layoutRow = layout.defaultRow
  1838.     object.layoutColumn = layout.defaultColumn
  1839.     containerAddChild(layout, object, ...)
  1840.  
  1841.     return object
  1842. end
  1843.  
  1844. function GUI.layout(x, y, width, height, columnCount, rowCount)
  1845.     local layout = GUI.container(x, y, width, height)
  1846.  
  1847.     layout.defaultRow = 1
  1848.     layout.defaultColumn = 1
  1849.  
  1850.     layout.addRow = layoutAddRow
  1851.     layout.addColumn = layoutAddColumn
  1852.     layout.removeRow = layoutRemoveRow
  1853.     layout.removeColumn = layoutRemoveColumn
  1854.  
  1855.     layout.setRowHeight = layoutSetRowHeight
  1856.     layout.setColumnWidth = layoutSetColumnWidth
  1857.  
  1858.     layout.setPosition = layoutSetPosition
  1859.     layout.setDirection = layoutSetDirection
  1860.     layout.setGridSize = layoutSetGridSize
  1861.     layout.setSpacing = layoutSetSpacing
  1862.     layout.setAlignment = layoutSetAlignment
  1863.     layout.setMargin = layoutSetMargin
  1864.     layout.getMargin = layoutGetMargin
  1865.    
  1866.     layout.fitToChildrenSize = layoutFitToChildrenSize
  1867.     layout.setFitting = layoutSetFitting
  1868.  
  1869.     layout.update = layoutUpdate
  1870.     layout.addChild = layoutAddChild
  1871.     layout.draw = layoutDraw
  1872.  
  1873.     layout:setGridSize(columnCount, rowCount)
  1874.  
  1875.     return layout
  1876. end
  1877.  
  1878. --------------------------------------------------------------------------------
  1879.  
  1880. local function filesystemDialogDraw(filesystemDialog)
  1881.     if filesystemDialog.extensionComboBox.hidden then
  1882.         filesystemDialog.input.width = filesystemDialog.cancelButton.localX - 4
  1883.     else
  1884.         filesystemDialog.input.width = filesystemDialog.extensionComboBox.localX - 3
  1885.     end
  1886.  
  1887.     if filesystemDialog.IOMode == GUI.IO_MODE_SAVE then
  1888.         filesystemDialog.submitButton.disabled = not filesystemDialog.input.text or filesystemDialog.input.text == ""
  1889.     else
  1890.         filesystemDialog.input.text = filesystemDialog.filesystemTree.selectedItem or ""
  1891.         filesystemDialog.submitButton.disabled = not filesystemDialog.filesystemTree.selectedItem
  1892.     end
  1893.    
  1894.     containerDraw(filesystemDialog)
  1895.     GUI.drawShadow(filesystemDialog.x, filesystemDialog.y, filesystemDialog.width, filesystemDialog.height, GUI.CONTEXT_MENU_SHADOW_TRANSPARENCY, true)
  1896.  
  1897.     return filesystemDialog
  1898. end
  1899.  
  1900. local function filesystemDialogSetMode(filesystemDialog, IOMode, filesystemMode)
  1901.     filesystemDialog.IOMode = IOMode
  1902.     filesystemDialog.filesystemMode = filesystemMode
  1903.  
  1904.     if filesystemDialog.IOMode == GUI.IO_MODE_SAVE then
  1905.         filesystemDialog.filesystemTree.showMode = GUI.IO_MODE_DIRECTORY
  1906.         filesystemDialog.filesystemTree.selectionMode = GUI.IO_MODE_DIRECTORY
  1907.         filesystemDialog.input.disabled = false
  1908.         filesystemDialog.extensionComboBox.hidden = filesystemDialog.filesystemMode ~= GUI.IO_MODE_FILE or not filesystemDialog.filesystemTree.extensionFilters
  1909.     else
  1910.         if filesystemDialog.filesystemMode == GUI.IO_MODE_FILE then
  1911.             filesystemDialog.filesystemTree.showMode = GUI.IO_MODE_BOTH
  1912.             filesystemDialog.filesystemTree.selectionMode = GUI.IO_MODE_FILE
  1913.         else
  1914.             filesystemDialog.filesystemTree.showMode = GUI.IO_MODE_DIRECTORY
  1915.             filesystemDialog.filesystemTree.selectionMode = GUI.IO_MODE_DIRECTORY
  1916.         end
  1917.  
  1918.         filesystemDialog.input.disabled = true
  1919.         filesystemDialog.extensionComboBox.hidden = true
  1920.     end
  1921. end
  1922.  
  1923. local function filesystemDialogAddExtensionFilter(filesystemDialog, extension)
  1924.     filesystemDialog.extensionComboBox:addItem(extension)
  1925.     filesystemDialog.extensionComboBox.width = math.max(filesystemDialog.extensionComboBox.width, unicode.len(extension) + 3)
  1926.     filesystemDialog.extensionComboBox.localX = filesystemDialog.cancelButton.localX - filesystemDialog.extensionComboBox.width - 2
  1927.     filesystemDialog.filesystemTree:addExtensionFilter(extension)
  1928.  
  1929.     filesystemDialog:setMode(filesystemDialog.IOMode, filesystemDialog.filesystemMode)
  1930. end
  1931.  
  1932. local function filesystemDialogExpandPath(filesystemDialog, ...)
  1933.     filesystemDialog.filesystemTree:expandPath(...)
  1934. end
  1935.  
  1936. function GUI.filesystemDialog(x, y, width, height, submitButtonText, cancelButtonText, placeholderText, path)
  1937.     local filesystemDialog = GUI.container(x, y, width, height)
  1938.    
  1939.     filesystemDialog:addChild(GUI.panel(1, height - 2, width, 3, 0xD2D2D2))
  1940.    
  1941.     filesystemDialog.cancelButton = filesystemDialog:addChild(GUI.adaptiveRoundedButton(1, height - 1, 1, 0, 0xE1E1E1, 0x3C3C3C, 0x3C3C3C, 0xE1E1E1, cancelButtonText))
  1942.     filesystemDialog.submitButton = filesystemDialog:addChild(GUI.adaptiveRoundedButton(1, height - 1, 1, 0, 0x3C3C3C, 0xE1E1E1, 0xE1E1E1, 0x3C3C3C, submitButtonText))
  1943.     filesystemDialog.submitButton.localX = filesystemDialog.width - filesystemDialog.submitButton.width - 1
  1944.     filesystemDialog.cancelButton.localX = filesystemDialog.submitButton.localX - filesystemDialog.cancelButton.width - 2
  1945.  
  1946.     filesystemDialog.extensionComboBox = filesystemDialog:addChild(GUI.comboBox(1, height - 1, 1, 1, 0xE1E1E1, 0x696969, 0xC3C3C3, 0x878787))
  1947.     filesystemDialog.extensionComboBox.hidden = true
  1948.  
  1949.     filesystemDialog.input = filesystemDialog:addChild(GUI.input(2, height - 1, 1, 1, 0xE1E1E1, 0x696969, 0x969696, 0xE1E1E1, 0x3C3C3C, "", placeholderText))
  1950.  
  1951.     filesystemDialog.filesystemTree = filesystemDialog:addChild(GUI.filesystemTree(1, 1, width, height - 3, 0xE1E1E1, 0x3C3C3C, 0x3C3C3C, 0xA5A5A5, 0x3C3C3C, 0xE1E1E1, 0xB4B4B4, 0xA5A5A5, 0xC3C3C3, 0x4B4B4B))
  1952.     filesystemDialog.filesystemTree.workPath = path
  1953.     filesystemDialog.animationDuration = GUI.FILESYSTEM_DIALOG_ANIMATION_DURATION
  1954.  
  1955.     filesystemDialog.draw = filesystemDialogDraw
  1956.     filesystemDialog.setMode = filesystemDialogSetMode
  1957.     filesystemDialog.addExtensionFilter = filesystemDialogAddExtensionFilter
  1958.  
  1959.     filesystemDialog.expandPath = filesystemDialogExpandPath
  1960.     filesystemDialog:setMode(GUI.IO_MODE_OPEN, GUI.IO_MODE_FILE)
  1961.  
  1962.     return filesystemDialog
  1963. end
  1964.  
  1965. local function filesystemDialogShow(filesystemDialog)
  1966.     filesystemDialog.filesystemTree:updateFileList()
  1967.     filesystemDialog:addAnimation(
  1968.         function(animation)
  1969.             filesystemDialog.localY = math.floor(1 + (1.0 - animation.position) * (-filesystemDialog.height))
  1970.         end,
  1971.         function(animation)
  1972.             animation:remove()
  1973.         end
  1974.     ):start(filesystemDialog.animationDuration)
  1975.  
  1976.     return filesystemDialog
  1977. end
  1978.  
  1979. --------------------------------------------------------------------------------
  1980.  
  1981. function GUI.addFilesystemDialog(parentContainer, addPanel, ...)
  1982.     local container = GUI.addBackgroundContainer(parentContainer, addPanel, false, nil)
  1983.  
  1984.     local filesystemDialog = container:addChild(GUI.filesystemDialog(1, 1, ...))
  1985.     filesystemDialog.localX = math.floor(container.width / 2 - filesystemDialog.width / 2)
  1986.     filesystemDialog.localY = -filesystemDialog.height
  1987.  
  1988.     local function onAnyTouch()
  1989.         container:remove()
  1990.         filesystemDialog.firstParent:draw()
  1991.     end
  1992.  
  1993.     filesystemDialog.cancelButton.onTouch = function()
  1994.         onAnyTouch()
  1995.  
  1996.         if filesystemDialog.onCancel then
  1997.             filesystemDialog.onCancel()
  1998.         end
  1999.     end
  2000.  
  2001.     filesystemDialog.submitButton.onTouch = function()
  2002.         onAnyTouch()
  2003.        
  2004.         local path = filesystemDialog.filesystemTree.selectedItem or filesystemDialog.filesystemTree.workPath or "/"
  2005.         if filesystemDialog.IOMode == GUI.IO_MODE_SAVE then
  2006.             path = path .. filesystemDialog.input.text
  2007.            
  2008.             if filesystemDialog.filesystemMode == GUI.IO_MODE_FILE then
  2009.                 local selectedItem = filesystemDialog.extensionComboBox:getItem(filesystemDialog.extensionComboBox.selectedItem)
  2010.                 path = path .. (selectedItem and selectedItem.text or "")
  2011.             else
  2012.                 path = path .. "/"
  2013.             end
  2014.         end
  2015.  
  2016.         if filesystemDialog.onSubmit then
  2017.             filesystemDialog.onSubmit(path)
  2018.         end
  2019.     end
  2020.  
  2021.     filesystemDialog.show = filesystemDialogShow
  2022.  
  2023.     return filesystemDialog
  2024. end
  2025.  
  2026. --------------------------------------------------------------------------------
  2027.  
  2028. local function filesystemChooserDraw(object)
  2029.     local tipWidth = object.height * 2 - 1
  2030.     local y = math.floor(object.y + object.height / 2)
  2031.    
  2032.     buffer.drawRectangle(object.x, object.y, object.width - tipWidth, object.height, object.colors.background, object.colors.text, " ")
  2033.     buffer.drawRectangle(object.x + object.width - tipWidth, object.y, tipWidth, object.height, object.pressed and object.colors.tipText or object.colors.tipBackground, object.pressed and object.colors.tipBackground or object.colors.tipText, " ")
  2034.     buffer.drawText(object.x + object.width - math.floor(tipWidth / 2) - 1, y, object.pressed and object.colors.tipBackground or object.colors.tipText, "…")
  2035.     buffer.drawText(object.x + 1, y, object.colors.text, string.limit(object.path or object.placeholderText, object.width - tipWidth - 2, "left"))
  2036.  
  2037.     return filesystemChooser
  2038. end
  2039.  
  2040. local function filesystemChooserAddExtensionFilter(object, extension)
  2041.     object.extensionFilters[unicode.lower(extension)] = true
  2042. end
  2043.  
  2044. local function filesystemChooserSetMode(object, IOMode, filesystemMode)
  2045.     object.IOMode = IOMode
  2046.     object.filesystemMode = filesystemMode
  2047. end
  2048.  
  2049. local function filesystemChooserEventHandler(application, object, e1)
  2050.     if e1 == "touch" then
  2051.         object.pressed = true
  2052.         application:draw()
  2053.  
  2054.         local filesystemDialog = GUI.addFilesystemDialog(application, false, 50, math.floor(application.height * 0.8), object.submitButtonText, object.cancelButtonText, object.placeholderText, object.filesystemDialogPath)
  2055.  
  2056.         for key in pairs(object.extensionFilters) do
  2057.             filesystemDialog:addExtensionFilter(key)
  2058.         end
  2059.  
  2060.         filesystemDialog:setMode(object.IOMode, object.filesystemMode)
  2061.  
  2062.         if object.path and #object.path > 0 then
  2063.             -- local path = object.path:gsub("/+", "/")
  2064.             filesystemDialog.filesystemTree.selectedItem = object.IOMode == GUI.IO_MODE_OPEN and object.path or filesystem.path(object.path)
  2065.             filesystemDialog.input.text = filesystem.name(object.path)
  2066.             filesystemDialog:expandPath(object.IOMode == GUI.IO_MODE_OPEN and filesystem.path(object.path) or filesystem.path(filesystem.path(object.path)))
  2067.         end
  2068.        
  2069.         filesystemDialog.onCancel = function()
  2070.             object.pressed = false
  2071.             application:draw()
  2072.         end
  2073.  
  2074.         filesystemDialog.onSubmit = function(path)
  2075.             object.path = path
  2076.             filesystemDialog.onCancel()
  2077.             if object.onSubmit then
  2078.                 object.onSubmit(object.path)
  2079.             end
  2080.         end
  2081.  
  2082.         filesystemDialog:show()
  2083.     end
  2084. end
  2085.  
  2086. function GUI.filesystemChooser(x, y, width, height, backgroundColor, textColor, tipBackgroundColor, tipTextColor, path, submitButtonText, cancelButtonText, placeholderText, filesystemDialogPath)
  2087.     local object = GUI.object(x, y, width, height)
  2088.    
  2089.     object.eventHandler = comboBoxEventHandler
  2090.     object.colors = {
  2091.         tipBackground = tipBackgroundColor,
  2092.         tipText = tipTextColor,
  2093.         text = textColor,
  2094.         background = backgroundColor
  2095.     }
  2096.  
  2097.     object.submitButtonText = submitButtonText
  2098.     object.cancelButtonText = cancelButtonText
  2099.     object.placeholderText = placeholderText
  2100.     object.pressed = false
  2101.     object.path = path
  2102.     object.filesystemDialogPath = filesystemDialogPath
  2103.     object.filesystemMode = GUI.IO_MODE_FILE
  2104.     object.IOMode = GUI.IO_MODE_OPEN
  2105.     object.extensionFilters = {}
  2106.  
  2107.     object.draw = filesystemChooserDraw
  2108.     object.eventHandler = filesystemChooserEventHandler
  2109.     object.addExtensionFilter = filesystemChooserAddExtensionFilter
  2110.     object.setMode = filesystemChooserSetMode
  2111.  
  2112.     return object
  2113. end
  2114.  
  2115. --------------------------------------------------------------------------------
  2116.  
  2117. local function resizerDraw(object)
  2118.     local horizontalMode, x, y, symbol = object.width >= object.height
  2119.  
  2120.     if horizontalMode then
  2121.         buffer.drawText(object.x, math.floor(object.y + object.height / 2), object.colors.helper, string.rep("━", object.width))
  2122.        
  2123.         if object.lastTouchX then
  2124.             buffer.drawText(object.lastTouchX, object.lastTouchY, object.colors.arrow, "↑")
  2125.         end
  2126.     else
  2127.         local x = math.floor(object.x + object.width / 2)
  2128.         local bufferWidth, bufferHeight, index = buffer.getResolution()
  2129.        
  2130.         for i = object.y, object.y + object.height - 1 do
  2131.             if x >= 1 and x <= bufferWidth and i >= 1 and i <= bufferHeight then
  2132.                 index = buffer.getIndex(x, i)
  2133.                 buffer.rawSet(index, buffer.rawGet(index), object.colors.helper, "┃")
  2134.             end
  2135.         end
  2136.  
  2137.         if object.lastTouchX then
  2138.             buffer.drawText(object.lastTouchX - 1, object.lastTouchY, object.colors.arrow, "←→")
  2139.         end
  2140.     end
  2141. end
  2142.  
  2143. local function resizerEventHandler(application, object, e1, e2, e3, e4)
  2144.     if e1 == "touch" then
  2145.         object.lastTouchX, object.lastTouchY = e3, e4
  2146.         application:draw()
  2147.     elseif e1 == "drag" and object.lastTouchX then     
  2148.         if object.onResize then
  2149.             object.onResize(e3 - object.lastTouchX, e4 - object.lastTouchY)
  2150.         end
  2151.        
  2152.         object.lastTouchX, object.lastTouchY = e3, e4
  2153.         application:draw()
  2154.     elseif e1 == "drop" then
  2155.         if object.onResizeFinished then
  2156.             object.onResizeFinished()
  2157.         end
  2158.  
  2159.         object.lastTouchX, object.lastTouchY = nil, nil
  2160.         application:draw()
  2161.     end
  2162. end
  2163.  
  2164. function GUI.resizer(x, y, width, height, helperColor, arrowColor)
  2165.     local object = GUI.object(x, y, width, height)
  2166.    
  2167.     object.colors = {
  2168.         helper = helperColor,
  2169.         arrow = arrowColor
  2170.     }
  2171.  
  2172.     object.draw = resizerDraw
  2173.     object.eventHandler = resizerEventHandler
  2174.  
  2175.     return object
  2176. end
  2177.  
  2178. --------------------------------------------------------------------------------
  2179.  
  2180. local function scrollBarDraw(scrollBar)
  2181.     local isVertical = scrollBar.height > scrollBar.width
  2182.     local valuesDelta = scrollBar.maximumValue - scrollBar.minimumValue
  2183.     local part = scrollBar.value / valuesDelta
  2184.  
  2185.     if isVertical then
  2186.         local barSize = math.ceil(scrollBar.shownValueCount / valuesDelta * scrollBar.height)
  2187.         local halfBarSize = math.floor(barSize / 2)
  2188.        
  2189.         scrollBar.ghostPosition.y = scrollBar.y + halfBarSize
  2190.         scrollBar.ghostPosition.height = scrollBar.height - barSize
  2191.  
  2192.         if scrollBar.thin then
  2193.             local y1 = math.floor(scrollBar.ghostPosition.y + part * scrollBar.ghostPosition.height - halfBarSize)
  2194.             local y2 = y1 + barSize - 1
  2195.             local background
  2196.  
  2197.             for y = scrollBar.y, scrollBar.y + scrollBar.height - 1 do
  2198.                 background = buffer.get(scrollBar.x, y)
  2199.                 buffer.set(scrollBar.x, y, background, y >= y1 and y <= y2 and scrollBar.colors.foreground or scrollBar.colors.background, "┃")
  2200.             end
  2201.         else
  2202.             buffer.drawRectangle(scrollBar.x, scrollBar.y, scrollBar.width, scrollBar.height, scrollBar.colors.background, scrollBar.colors.foreground, " ")
  2203.             buffer.drawRectangle(
  2204.                 scrollBar.x,
  2205.                 math.floor(scrollBar.ghostPosition.y + part * scrollBar.ghostPosition.height - halfBarSize),
  2206.                 scrollBar.width,
  2207.                 barSize,
  2208.                 scrollBar.colors.foreground, 0x0, " "
  2209.             )
  2210.         end
  2211.     else
  2212.         local barSize = math.ceil(scrollBar.shownValueCount / valuesDelta * scrollBar.width)
  2213.         local halfBarSize = math.floor(barSize / 2)
  2214.        
  2215.         scrollBar.ghostPosition.x = scrollBar.x + halfBarSize
  2216.         scrollBar.ghostPosition.width = scrollBar.width - barSize
  2217.  
  2218.         if scrollBar.thin then
  2219.             local x1 = math.floor(scrollBar.ghostPosition.x + part * scrollBar.ghostPosition.width - halfBarSize)
  2220.             local x2 = x1 + barSize - 1
  2221.             local background
  2222.  
  2223.             for x = scrollBar.x, scrollBar.x + scrollBar.width - 1 do
  2224.                 background = buffer.get(x, scrollBar.y)
  2225.                 buffer.set(x, scrollBar.y, background, x >= x1 and x <= x2 and scrollBar.colors.foreground or scrollBar.colors.background, "⠤")
  2226.             end
  2227.         else
  2228.             buffer.drawRectangle(scrollBar.x, scrollBar.y, scrollBar.width, scrollBar.height, scrollBar.colors.background, scrollBar.colors.foreground, " ")
  2229.             buffer.drawRectangle(
  2230.                 math.floor(scrollBar.ghostPosition.x + part * scrollBar.ghostPosition.width - halfBarSize),
  2231.                 scrollBar.y,
  2232.                 barSize,
  2233.                 scrollBar.height,
  2234.                 scrollBar.colors.foreground, 0x0, " "
  2235.             )
  2236.         end
  2237.     end
  2238.  
  2239.     return scrollBar
  2240. end
  2241.  
  2242. local function scrollBarEventHandler(application, object, e1, e2, e3, e4, e5, ...)
  2243.     local newValue = object.value
  2244.  
  2245.     if e1 == "touch" or e1 == "drag" then
  2246.         if object.height > object.width then
  2247.             if e4 == object.y + object.height - 1 then
  2248.                 newValue = object.maximumValue
  2249.             else
  2250.                 newValue = object.minimumValue + (e4 - object.y) / object.height * (object.maximumValue - object.minimumValue)
  2251.             end
  2252.         else
  2253.             if e3 == object.x + object.width - 1 then
  2254.                 newValue = object.maximumValue
  2255.             else
  2256.                 newValue = object.minimumValue + (e3 - object.x) / object.width * (object.maximumValue - object.minimumValue)
  2257.             end
  2258.         end
  2259.     elseif e1 == "scroll" then
  2260.         if e5 == 1 then
  2261.             if object.value >= object.minimumValue + object.onScrollValueIncrement then
  2262.                 newValue = object.value - object.onScrollValueIncrement
  2263.             else
  2264.                 newValue = object.minimumValue
  2265.             end
  2266.         else
  2267.             if object.value <= object.maximumValue - object.onScrollValueIncrement then
  2268.                 newValue = object.value + object.onScrollValueIncrement
  2269.             else
  2270.                 newValue = object.maximumValue
  2271.             end
  2272.         end
  2273.     end
  2274.  
  2275.     if e1 == "touch" or e1 == "drag" or e1 == "scroll" then
  2276.         object.value = newValue
  2277.         if object.onTouch then
  2278.             object.onTouch(application, object, e1, e2, e3, e4, e5, ...)
  2279.         end
  2280.  
  2281.         application:draw()
  2282.     end
  2283. end
  2284.  
  2285. function GUI.scrollBar(x, y, width, height, backgroundColor, foregroundColor, minimumValue, maximumValue, value, shownValueCount, onScrollValueIncrement, thin)
  2286.     local scrollBar = GUI.object(x, y, width, height)
  2287.  
  2288.     scrollBar.eventHandler = scrollBarEventHandler
  2289.     scrollBar.maximumValue = maximumValue
  2290.     scrollBar.minimumValue = minimumValue
  2291.     scrollBar.value = value
  2292.     scrollBar.onScrollValueIncrement = onScrollValueIncrement
  2293.     scrollBar.shownValueCount = shownValueCount
  2294.     scrollBar.thin = thin
  2295.     scrollBar.colors = {
  2296.         background = backgroundColor,
  2297.         foreground = foregroundColor,
  2298.     }
  2299.     scrollBar.ghostPosition = {}
  2300.     scrollBar.draw = scrollBarDraw
  2301.  
  2302.     return scrollBar
  2303. end
  2304.  
  2305. --------------------------------------------------------------------------------
  2306.  
  2307. local function treeDraw(tree)  
  2308.     local y, yEnd, showScrollBar = tree.y, tree.y + tree.height - 1, #tree.items > tree.height
  2309.     local textLimit = tree.width - (showScrollBar and 1 or 0)
  2310.  
  2311.     if tree.colors.default.background then
  2312.         buffer.drawRectangle(tree.x, tree.y, tree.width, tree.height, tree.colors.default.background, tree.colors.default.expandable, " ")
  2313.     end
  2314.  
  2315.     for i = tree.fromItem, #tree.items do
  2316.         local textColor, arrowColor, text = tree.colors.default.notExpandable, tree.colors.default.arrow, tree.items[i].expandable and "■ " or "□ "
  2317.  
  2318.         if tree.selectedItem == tree.items[i].definition then
  2319.             textColor, arrowColor = tree.colors.selected.any, tree.colors.selected.arrow
  2320.             buffer.drawRectangle(tree.x, y, tree.width, 1, tree.colors.selected.background, textColor, " ")
  2321.         else
  2322.             if tree.items[i].expandable then
  2323.                 textColor = tree.colors.default.expandable
  2324.             elseif tree.items[i].disabled then
  2325.                 textColor = tree.colors.disabled
  2326.             end
  2327.         end
  2328.  
  2329.         if tree.items[i].expandable then
  2330.             buffer.drawText(tree.x + tree.items[i].offset, y, arrowColor, tree.expandedItems[tree.items[i].definition] and "▽" or "▷")
  2331.         end
  2332.  
  2333.         buffer.drawText(tree.x + tree.items[i].offset + 2, y, textColor, unicode.sub(text .. tree.items[i].name, 1, textLimit - tree.items[i].offset - 2))
  2334.  
  2335.         y = y + 1
  2336.         if y > yEnd then break end
  2337.     end
  2338.  
  2339.     if showScrollBar then
  2340.         local scrollBar = tree.scrollBar
  2341.         scrollBar.x = tree.x + tree.width - 1
  2342.         scrollBar.y = tree.y
  2343.         scrollBar.width = 1
  2344.         scrollBar.height = tree.height
  2345.         scrollBar.colors.background = tree.colors.scrollBar.background
  2346.         scrollBar.colors.foreground = tree.colors.scrollBar.foreground
  2347.         scrollBar.minimumValue = 1
  2348.         scrollBar.maximumValue = #tree.items
  2349.         scrollBar.value = tree.fromItem
  2350.         scrollBar.shownValueCount = tree.height
  2351.         scrollBar.onScrollValueIncrement = 1
  2352.         scrollBar.thin = true
  2353.  
  2354.         scrollBar:draw()
  2355.     end
  2356.  
  2357.     return tree
  2358. end
  2359.  
  2360. local function treeEventHandler(application, tree, e1, e2, e3, e4, e5, ...)
  2361.     if e1 == "touch" then
  2362.         local i = e4 - tree.y + tree.fromItem
  2363.         if tree.items[i] then
  2364.             if
  2365.                 tree.items[i].expandable and
  2366.                 (
  2367.                     tree.selectionMode == GUI.IO_MODE_FILE or
  2368.                     e3 >= tree.x + tree.items[i].offset - 1 and e3 <= tree.x + tree.items[i].offset + 1
  2369.                 )
  2370.             then
  2371.                 if tree.expandedItems[tree.items[i].definition] then
  2372.                     tree.expandedItems[tree.items[i].definition] = nil
  2373.                 else
  2374.                     tree.expandedItems[tree.items[i].definition] = true
  2375.                 end
  2376.  
  2377.                 if tree.onItemExpanded then
  2378.                     tree.onItemExpanded(tree.selectedItem, e1, e2, e3, e4, e5, ...)
  2379.                 end
  2380.             else
  2381.                 if
  2382.                     (
  2383.                         tree.selectionMode == GUI.IO_MODE_BOTH or
  2384.                         tree.selectionMode == GUI.IO_MODE_DIRECTORY and tree.items[i].expandable or
  2385.                         tree.selectionMode == GUI.IO_MODE_FILE
  2386.                     ) and not tree.items[i].disabled
  2387.                 then
  2388.                     tree.selectedItem = tree.items[i].definition
  2389.  
  2390.                     if tree.onItemSelected then
  2391.                         tree.onItemSelected(tree.selectedItem, e1, e2, e3, e4, e5, ...)
  2392.                     end
  2393.                 end
  2394.             end
  2395.  
  2396.             application:draw()
  2397.         end
  2398.     elseif e1 == "scroll" then
  2399.         if e5 == 1 then
  2400.             if tree.fromItem > 1 then
  2401.                 tree.fromItem = tree.fromItem - 1
  2402.                 application:draw()
  2403.             end
  2404.         else
  2405.             if tree.fromItem < #tree.items then
  2406.                 tree.fromItem = tree.fromItem + 1
  2407.                 application:draw()
  2408.             end
  2409.         end
  2410.     end
  2411. end
  2412.  
  2413. local function treeAddItem(tree, name, definition, offset, expandable, disabled)
  2414.     local item = {
  2415.         name = name,
  2416.         expandable = expandable,
  2417.         offset = offset or 0,
  2418.         definition = definition,
  2419.         disabled = disabled
  2420.     }
  2421.     table.insert(tree.items, item)
  2422.  
  2423.     return item
  2424. end
  2425.  
  2426. function GUI.tree(x, y, width, height, backgroundColor, expandableColor, notExpandableColor, arrowColor, backgroundSelectedColor, anySelectionColor, arrowSelectionColor, disabledColor, scrollBarBackground, scrollBarForeground, showMode, selectionMode)
  2427.     local tree = GUI.object(x, y, width, height)
  2428.    
  2429.     tree.eventHandler = treeEventHandler
  2430.     tree.colors = {
  2431.         default = {
  2432.             background = backgroundColor,
  2433.             expandable = expandableColor,
  2434.             notExpandable = notExpandableColor,
  2435.             arrow = arrowColor,
  2436.         },
  2437.         selected = {
  2438.             background = backgroundSelectedColor,
  2439.             any = anySelectionColor,
  2440.             arrow = arrowSelectionColor,
  2441.         },
  2442.         scrollBar = {
  2443.             background = scrollBarBackground,
  2444.             foreground = scrollBarForeground
  2445.         },
  2446.         disabled = disabledColor
  2447.     }
  2448.     tree.items = {}
  2449.     tree.fromItem = 1
  2450.     tree.selectedItem = nil
  2451.     tree.expandedItems = {}
  2452.  
  2453.     tree.scrollBar = GUI.scrollBar(1, 1, 1, 1, 0x0, 0x0, 1, 1, 1, 1, 1)
  2454.  
  2455.     tree.showMode = showMode
  2456.     tree.selectionMode = selectionMode
  2457.     tree.eventHandler = treeEventHandler
  2458.     tree.addItem = treeAddItem
  2459.     tree.draw = treeDraw
  2460.  
  2461.     return tree
  2462. end
  2463.  
  2464. --------------------------------------------------------------------------------
  2465.  
  2466. local function filesystemTreeUpdateFileListRecursively(tree, path, offset)
  2467.     local list = {}
  2468.     for file in filesystem.list(path) do
  2469.         table.insert(list, file)
  2470.     end
  2471.  
  2472.     local i, expandables = 1, {}
  2473.     while i <= #list do
  2474.         if filesystem.isDirectory(path .. list[i]) then
  2475.             table.insert(expandables, list[i])
  2476.             table.remove(list, i)
  2477.         else
  2478.             i = i + 1
  2479.         end
  2480.     end
  2481.  
  2482.     table.sort(expandables, function(a, b) return unicode.lower(a) < unicode.lower(b) end)
  2483.     table.sort(list, function(a, b) return unicode.lower(a) < unicode.lower(b) end)
  2484.  
  2485.     if tree.showMode == GUI.IO_MODE_BOTH or tree.showMode == GUI.IO_MODE_DIRECTORY then
  2486.         for i = 1, #expandables do
  2487.             tree:addItem(filesystem.name(expandables[i]), path .. expandables[i], offset, true)
  2488.  
  2489.             if tree.expandedItems[path .. expandables[i]] then
  2490.                 filesystemTreeUpdateFileListRecursively(tree, path .. expandables[i], offset + 2)
  2491.             end
  2492.         end
  2493.     end
  2494.  
  2495.     if tree.showMode == GUI.IO_MODE_BOTH or tree.showMode == GUI.IO_MODE_FILE then
  2496.         for i = 1, #list do
  2497.             tree:addItem(list[i], path .. list[i], offset, false, tree.extensionFilters and not tree.extensionFilters[filesystem.extension(path .. list[i], true)] or false)
  2498.         end
  2499.     end
  2500. end
  2501.  
  2502. local function filesystemTreeUpdateFileList(tree)
  2503.     tree.items = {}
  2504.     filesystemTreeUpdateFileListRecursively(tree, tree.workPath, 1)
  2505. end
  2506.  
  2507. local function filesystemTreeAddExtensionFilter(tree, extensionFilter)
  2508.     tree.extensionFilters = tree.extensionFilters or {}
  2509.     tree.extensionFilters[unicode.lower(extensionFilter)] = true
  2510. end
  2511.  
  2512. local function filesystemTreeExpandPath(tree, path)
  2513.     local blyadina = tree.workPath
  2514.     for pizda in path:gmatch("[^/]+") do
  2515.         blyadina = blyadina .. pizda .. "/"
  2516.         tree.expandedItems[blyadina] = true
  2517.     end
  2518. end
  2519.  
  2520. function GUI.filesystemTree(...)
  2521.     local tree = GUI.tree(...)
  2522.  
  2523.     tree.workPath = "/"
  2524.     tree.updateFileList = filesystemTreeUpdateFileList
  2525.     tree.addExtensionFilter = filesystemTreeAddExtensionFilter
  2526.     tree.expandPath = filesystemTreeExpandPath
  2527.     tree.onItemExpanded = function()
  2528.         tree:updateFileList()
  2529.     end
  2530.  
  2531.     return tree
  2532. end
  2533.  
  2534. --------------------------------------------------------------------------------
  2535.  
  2536. local function textBoxUpdate(object)
  2537.     local doubleVerticalOffset = object.offset.vertical * 2
  2538.     object.textWidth = object.width - object.offset.horizontal * 2 - (object.scrollBarEnabled and 1 or 0)
  2539.  
  2540.     object.linesCopy = {}
  2541.  
  2542.     if object.autoWrap then
  2543.         for i = 1, #object.lines do
  2544.             local isTable = type(object.lines[i]) == "table"
  2545.             for subLine in (isTable and object.lines[i].text or object.lines[i]):gmatch("[^\n]+") do
  2546.                 local wrappedLine = string.wrap(subLine, object.textWidth)
  2547.                 for j = 1, #wrappedLine do
  2548.                     table.insert(object.linesCopy, isTable and {text = wrappedLine[j], color = object.lines[i].color} or wrappedLine[j])
  2549.                 end
  2550.             end
  2551.         end
  2552.     else
  2553.         for i = 1, #object.lines do
  2554.             table.insert(object.linesCopy, object.lines[i])
  2555.         end
  2556.     end
  2557.  
  2558.     if object.autoHeight then
  2559.         object.height = #object.linesCopy + doubleVerticalOffset
  2560.     end
  2561.  
  2562.     object.textHeight = object.height - doubleVerticalOffset
  2563. end
  2564.  
  2565. local function textBoxDraw(object)
  2566.     object:update()
  2567.  
  2568.     if object.colors.background then
  2569.         buffer.drawRectangle(object.x, object.y, object.width, object.height, object.colors.background, object.colors.text, " ", object.colors.transparency)
  2570.     end
  2571.  
  2572.     local x, y = nil, object.y + object.offset.vertical
  2573.     local lineType, text, textColor
  2574.     for i = object.currentLine, object.currentLine + object.textHeight - 1 do
  2575.         if object.linesCopy[i] then
  2576.             lineType = type(object.linesCopy[i])
  2577.             if lineType == "string" then
  2578.                 text, textColor = string.limit(object.linesCopy[i], object.textWidth), object.colors.text
  2579.             elseif lineType == "table" then
  2580.                 text, textColor = string.limit(object.linesCopy[i].text, object.textWidth), object.linesCopy[i].color
  2581.             else
  2582.                 error("Unknown TextBox line type: " .. tostring(lineType))
  2583.             end
  2584.  
  2585.             x = GUI.getAlignmentCoordinates(
  2586.                 object.x + object.offset.horizontal,
  2587.                 1,
  2588.                 object.textWidth,
  2589.                 1,
  2590.                 object.horizontalAlignment,
  2591.                 object.verticalAlignment,
  2592.                 unicode.len(text),
  2593.                 1
  2594.             )
  2595.  
  2596.             buffer.drawText(math.floor(x), y, textColor, text)
  2597.             y = y + 1
  2598.         else
  2599.             break
  2600.         end
  2601.     end
  2602.  
  2603.     if object.scrollBarEnabled and object.textHeight < #object.lines then
  2604.         object.scrollBar.x = object.x + object.width - 1
  2605.         object.scrollBar.y = object.y
  2606.         object.scrollBar.height = object.height
  2607.         object.scrollBar.maximumValue = #object.lines - object.textHeight + 1
  2608.         object.scrollBar.value = object.currentLine
  2609.         object.scrollBar.shownValueCount = object.textHeight
  2610.  
  2611.         object.scrollBar:draw()
  2612.     end
  2613.  
  2614.     return object
  2615. end
  2616.  
  2617. local function scrollDownTextBox(object, count)
  2618.     count = math.min(count or 1, #object.lines - object.height - object.currentLine + object.offset.vertical * 2 + 1)
  2619.     if #object.lines >= object.height and object.currentLine < #object.lines - count then
  2620.         object.currentLine = object.currentLine + count
  2621.     end
  2622.  
  2623.     return object
  2624. end
  2625.  
  2626. local function scrollUpTextBox(object, count)
  2627.     count = count or 1
  2628.     if object.currentLine > count and object.currentLine >= 1 then object.currentLine = object.currentLine - count end
  2629.     return object
  2630. end
  2631.  
  2632. local function scrollToStartTextBox(object)
  2633.     object.currentLine = 1
  2634.     return object
  2635. end
  2636.  
  2637. local function scrollToEndTextBox(object)
  2638.     if #object.lines > object.textHeight then
  2639.         object.currentLine = #object.lines - object.textHeight + 1
  2640.     end
  2641.  
  2642.     return object
  2643. end
  2644.  
  2645. local function textBoxScrollEventHandler(application, object, e1, e2, e3, e4, e5)
  2646.     if e1 == "scroll" then
  2647.         if e5 == 1 then
  2648.             object:scrollUp()
  2649.         else
  2650.             object:scrollDown()
  2651.         end
  2652.  
  2653.         application:draw()
  2654.     end
  2655. end
  2656.  
  2657. function GUI.textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset, autoWrap, autoHeight)
  2658.     local object = GUI.object(x, y, width, height)
  2659.    
  2660.     object.colors = {
  2661.         text = textColor,
  2662.         background = backgroundColor
  2663.     }
  2664.     object.lines = lines
  2665.     object.currentLine = currentLine or 1
  2666.     object.scrollUp = scrollUpTextBox
  2667.     object.scrollDown = scrollDownTextBox
  2668.     object.scrollToStart = scrollToStartTextBox
  2669.     object.scrollToEnd = scrollToEndTextBox
  2670.     object.offset = {horizontal = horizontalOffset or 0, vertical = verticalOffset or 0}
  2671.     object.autoWrap = autoWrap
  2672.     object.autoHeight = autoHeight
  2673.     object.scrollBar = GUI.scrollBar(1, 1, 1, 1, 0xC3C3C3, 0x4B4B4B, 1, 1, 1, 1, 1, true)
  2674.     object.scrollBarEnabled = false
  2675.    
  2676.     object.eventHandler = textBoxScrollEventHandler
  2677.     object.draw = textBoxDraw
  2678.     object.update = textBoxUpdate
  2679.  
  2680.     object.setAlignment = GUI.setAlignment
  2681.     object:setAlignment(GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
  2682.     object:update()
  2683.  
  2684.     return object
  2685. end
  2686.  
  2687. --------------------------------------------------------------------------------
  2688.  
  2689. local function inputSetCursorPosition(input, newPosition)
  2690.     if newPosition < 1 then
  2691.         newPosition = 1
  2692.     elseif newPosition > unicode.len(input.text) + 1 then
  2693.         newPosition = unicode.len(input.text) + 1
  2694.     end
  2695.  
  2696.     if newPosition > input.textCutFrom + input.width - 1 - input.textOffset * 2 then
  2697.         input.textCutFrom = input.textCutFrom + newPosition - (input.textCutFrom + input.width - 1 - input.textOffset * 2)
  2698.     elseif newPosition < input.textCutFrom then
  2699.         input.textCutFrom = newPosition
  2700.     end
  2701.  
  2702.     input.cursorPosition = newPosition
  2703.  
  2704.     return input
  2705. end
  2706.  
  2707. local function inputTextDrawMethod(x, y, color, text)
  2708.     buffer.drawText(x, y, color, text)
  2709. end
  2710.  
  2711. local function inputDraw(input)
  2712.     local background, foreground, transparency, text
  2713.     if input.focused then
  2714.         background, transparency = input.colors.focused.background, input.colors.focused.transparency
  2715.         if input.text == "" then
  2716.             input.textCutFrom = 1
  2717.             foreground, text = input.colors.placeholderText, input.text
  2718.         else
  2719.             foreground = input.colors.focused.text
  2720.             if input.textMask then
  2721.                 text = string.rep(input.textMask, unicode.len(input.text))
  2722.             else
  2723.                 text = input.text
  2724.             end
  2725.         end
  2726.     else
  2727.         background, transparency = input.colors.default.background, input.colors.default.transparency
  2728.         if input.text == "" then
  2729.             input.textCutFrom = 1
  2730.             foreground, text = input.colors.placeholderText, input.placeholderText
  2731.         else
  2732.             foreground = input.colors.default.text
  2733.             if input.textMask then
  2734.                 text = string.rep(input.textMask, unicode.len(input.text))
  2735.             else
  2736.                 text = input.text
  2737.             end
  2738.         end
  2739.     end
  2740.  
  2741.     if background then
  2742.         buffer.drawRectangle(input.x, input.y, input.width, input.height, background, foreground, " ", transparency)
  2743.     end
  2744.  
  2745.     local y = input.y + math.floor(input.height / 2)
  2746.  
  2747.     input.textDrawMethod(
  2748.         input.x + input.textOffset,
  2749.         y,
  2750.         foreground,
  2751.         unicode.sub(
  2752.             text or "",
  2753.             input.textCutFrom,
  2754.             input.textCutFrom + input.width - 1 - input.textOffset * 2
  2755.         )
  2756.     )
  2757.  
  2758.     if input.cursorBlinkState then
  2759.         local index = buffer.getIndex(input.x + input.cursorPosition - input.textCutFrom + input.textOffset, y)
  2760.         local background = buffer.rawGet(index)
  2761.         buffer.rawSet(index, background, input.colors.cursor, input.cursorSymbol)
  2762.     end
  2763. end
  2764.  
  2765. local function inputCursorBlink(application, input, state)
  2766.     input.cursorBlinkState = state
  2767.     input.cursorBlinkUptime = computer.uptime()
  2768.     application:draw()
  2769. end
  2770.  
  2771. local function inputStopInput(application, input)
  2772.     input.stopInputObject:remove()
  2773.     input.focused = false
  2774.  
  2775.     if input.validator then
  2776.         if not input.validator(input.text) then
  2777.             input.text = input.startText
  2778.             input.startText = nil
  2779.  
  2780.             input:setCursorPosition(unicode.len(input.text) + 1)
  2781.         end
  2782.     end
  2783.    
  2784.     if input.onInputFinished then
  2785.         input.onInputFinished(application, input)
  2786.     end
  2787.  
  2788.     inputCursorBlink(application, input, false)
  2789. end
  2790.  
  2791. local function inputStartInput(input)
  2792.     input.startText = input.text
  2793.     input.focused = true
  2794.  
  2795.     if input.historyEnabled then
  2796.         input.historyIndex = input.historyIndex + 1
  2797.     end
  2798.  
  2799.     if input.eraseTextOnFocus then
  2800.         input.text = ""
  2801.     end
  2802.    
  2803.     input:setCursorPosition(unicode.len(input.text) + 1)
  2804.  
  2805.     input.stopInputObject.width, input.stopInputObject.height = input.firstParent.width, input.firstParent.height
  2806.     input.firstParent:addChild(input.stopInputObject)
  2807.  
  2808.     inputCursorBlink(input.firstParent, input, true)
  2809. end
  2810.  
  2811. local function inputEventHandler(application, input, e1, e2, e3, e4, e5, e6)
  2812.     if e1 == "touch" or e1 == "drag" then
  2813.         if input.focused then
  2814.             input:setCursorPosition(input.textCutFrom + e3 - input.x - input.textOffset)
  2815.             inputCursorBlink(application, input, true)
  2816.         else
  2817.             input:startInput()
  2818.         end
  2819.     elseif e1 == "key_down" and input.focused then
  2820.         application:consumeEvent()
  2821.  
  2822.         -- Return
  2823.         if e4 == 28 then
  2824.             if input.historyEnabled then
  2825.                 -- Очистка истории
  2826.                 for i = 1, (#input.history - input.historyLimit) do
  2827.                     table.remove(input.history, 1)
  2828.                 end
  2829.  
  2830.                 -- Добавление введенных данных в историю
  2831.                 if input.history[#input.history] ~= input.text and unicode.len(input.text) > 0 then
  2832.                     table.insert(input.history, input.text)
  2833.                 end
  2834.                 input.historyIndex = #input.history
  2835.             end
  2836.  
  2837.             inputStopInput(application, input)
  2838.             return
  2839.         -- Arrows up/down/left/right
  2840.         elseif e4 == 200 then
  2841.             if input.historyEnabled and #input.history > 0 then
  2842.                 -- Добавление уже введенного текста в историю при стрелке вверх
  2843.                 if input.historyIndex == #input.history + 1 and unicode.len(input.text) > 0 then
  2844.                     input.history[input.historyIndex] = input.text
  2845.                 end
  2846.  
  2847.                 input.historyIndex = input.historyIndex - 1
  2848.                 if input.historyIndex > #input.history then
  2849.                     input.historyIndex = #input.history
  2850.                 elseif input.historyIndex < 1 then
  2851.                     input.historyIndex = 1
  2852.                 end
  2853.  
  2854.                 input.text = input.history[input.historyIndex]
  2855.                 input:setCursorPosition(unicode.len(input.text) + 1)
  2856.             end
  2857.         elseif e4 == 208 then
  2858.             if input.historyEnabled and #input.history > 0 then
  2859.                 input.historyIndex = input.historyIndex + 1
  2860.                 if input.historyIndex > #input.history then
  2861.                     input.historyIndex = #input.history
  2862.                 elseif input.historyIndex < 1 then
  2863.                     input.historyIndex = 1
  2864.                 end
  2865.                
  2866.                 input.text = input.history[input.historyIndex]
  2867.                 input:setCursorPosition(unicode.len(input.text) + 1)
  2868.             end
  2869.         elseif e4 == 203 then
  2870.             input:setCursorPosition(input.cursorPosition - 1)
  2871.         elseif e4 == 205 then  
  2872.             input:setCursorPosition(input.cursorPosition + 1)
  2873.         -- Backspace
  2874.         elseif e4 == 14 then
  2875.             input.text = unicode.sub(unicode.sub(input.text, 1, input.cursorPosition - 1), 1, -2) .. unicode.sub(input.text, input.cursorPosition, -1)
  2876.             input:setCursorPosition(input.cursorPosition - 1)
  2877.         -- Delete
  2878.         elseif e4 == 211 then
  2879.             input.text = unicode.sub(input.text, 1, input.cursorPosition - 1) .. unicode.sub(input.text, input.cursorPosition + 1, -1)
  2880.         else
  2881.             local char = unicode.char(e3)
  2882.             if not keyboard.isControl(e3) then
  2883.                 input.text = unicode.sub(input.text, 1, input.cursorPosition - 1) .. char .. unicode.sub(input.text, input.cursorPosition, -1)
  2884.                 input:setCursorPosition(input.cursorPosition + 1)
  2885.             end
  2886.         end
  2887.  
  2888.         inputCursorBlink(application, input, true)
  2889.     elseif e1 == "clipboard" and input.focused then
  2890.         input.text = unicode.sub(input.text, 1, input.cursorPosition - 1) .. e3 .. unicode.sub(input.text, input.cursorPosition, -1)
  2891.         input:setCursorPosition(input.cursorPosition + unicode.len(e3))
  2892.        
  2893.         inputCursorBlink(application, input, true)
  2894.         application:consumeEvent()
  2895.     elseif not e1 and input.focused and computer.uptime() - input.cursorBlinkUptime > input.cursorBlinkDelay then
  2896.         inputCursorBlink(application, input, not input.cursorBlinkState)
  2897.     end
  2898. end
  2899.  
  2900. function GUI.input(x, y, width, height, backgroundColor, textColor, placeholderTextColor, backgroundFocusedColor, textFocusedColor, text, placeholderText, eraseTextOnFocus, textMask)
  2901.     local input = GUI.object(x, y, width, height)
  2902.    
  2903.     input.colors = {
  2904.         default = {
  2905.             background = backgroundColor,
  2906.             text = textColor
  2907.         },
  2908.         focused = {
  2909.             background = backgroundFocusedColor,
  2910.             text = textFocusedColor
  2911.         },
  2912.         placeholderText = placeholderTextColor,
  2913.         cursor = 0x00A8FF
  2914.     }
  2915.  
  2916.     input.text = text or ""
  2917.     input.placeholderText = placeholderText
  2918.     input.eraseTextOnFocus = eraseTextOnFocus
  2919.     input.textMask = textMask
  2920.  
  2921.     input.textOffset = 1
  2922.     input.textCutFrom = 1
  2923.     input.cursorPosition = 1
  2924.     input.cursorSymbol = "┃"
  2925.     input.cursorBlinkDelay = 0.4
  2926.     input.cursorBlinkState = false
  2927.     input.textMask = textMask
  2928.     input.setCursorPosition = inputSetCursorPosition
  2929.  
  2930.     input.history = {}
  2931.     input.historyLimit = 20
  2932.     input.historyIndex = 0
  2933.     input.historyEnabled = false
  2934.  
  2935.     input.stopInputObject = GUI.object(1, 1, 1, 1)
  2936.     input.stopInputObject.eventHandler = function(application, object, e1, e2, e3, e4, ...)
  2937.         if e1 == "touch" or e1 == "drop" then
  2938.             if
  2939.                 e3 >= input.x and
  2940.                 e3 < input.x + input.width and
  2941.                 e4 >= input.y and
  2942.                 e4 < input.y + input.height
  2943.             then
  2944.                 input.eventHandler(application, input, e1, e2, e3, e4, ...)
  2945.             else
  2946.                 inputStopInput(application, input)
  2947.             end
  2948.         end
  2949.     end
  2950.  
  2951.     input.textDrawMethod = inputTextDrawMethod
  2952.     input.draw = inputDraw
  2953.     input.eventHandler = inputEventHandler
  2954.     input.startInput = inputStartInput
  2955.  
  2956.     return input
  2957. end
  2958.  
  2959. --------------------------------------------------------------------------------
  2960.  
  2961. local function autoCompleteDraw(object)
  2962.     local y, yEnd = object.y, object.y + object.height - 1
  2963.  
  2964.     buffer.drawRectangle(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ")
  2965.  
  2966.     for i = object.fromItem, object.itemCount do
  2967.         local textColor, textMatchColor = object.colors.default.text, object.colors.default.textMatch
  2968.         if i == object.selectedItem then
  2969.             buffer.drawRectangle(object.x, y, object.width, 1, object.colors.selected.background, object.colors.selected.text, " ")
  2970.             textColor, textMatchColor = object.colors.selected.text, object.colors.selected.textMatch
  2971.         end
  2972.  
  2973.         buffer.drawText(object.x + 1, y, textMatchColor, unicode.sub(object.matchText, 1, object.width - 2))
  2974.         buffer.drawText(object.x + object.matchTextLength + 1, y, textColor, unicode.sub(object.items[i], object.matchTextLength + 1, object.matchTextLength + object.width - object.matchTextLength - 2))
  2975.  
  2976.         y = y + 1
  2977.         if y > yEnd then
  2978.             break
  2979.         end
  2980.     end
  2981.  
  2982.     if object.itemCount > object.height then
  2983.         object.scrollBar.x = object.x + object.width - 1
  2984.         object.scrollBar.y = object.y
  2985.         object.scrollBar.height = object.height
  2986.         object.scrollBar.maximumValue = object.itemCount - object.height + 2
  2987.         object.scrollBar.value = object.fromItem
  2988.         object.scrollBar.shownValueCount = object.height
  2989.  
  2990.         object.scrollBar:draw()
  2991.     end
  2992. end
  2993.  
  2994. local function autoCompleteScroll(application, object, direction)
  2995.     if object.itemCount >= object.height then
  2996.         object.fromItem = object.fromItem + direction
  2997.         if object.fromItem < 1 then
  2998.             object.fromItem = 1
  2999.         elseif object.fromItem > object.itemCount - object.height + 1 then
  3000.             object.fromItem = object.itemCount - object.height + 1
  3001.         end
  3002.     end
  3003. end
  3004.  
  3005. local function autoCompleteEventHandler(application, object, e1, e2, e3, e4, e5, ...)
  3006.     if e1 == "touch" then
  3007.         object.selectedItem = e4 - object.y + object.fromItem
  3008.         application:draw()
  3009.  
  3010.         if object.onItemSelected then
  3011.             os.sleep(0.2)
  3012.             object.onItemSelected(application, object, e1, e2, e3, e4, e5, ...)
  3013.         end
  3014.     elseif e1 == "scroll" then
  3015.         autoCompleteScroll(application, object, -e5)
  3016.         application:draw()
  3017.     elseif e1 == "key_down" then
  3018.         if e4 == 28 then
  3019.             if object.onItemSelected then
  3020.                 object.onItemSelected(application, object, e1, e2, e3, e4, e5, ...)
  3021.             end
  3022.         elseif e4 == 200 then
  3023.             object.selectedItem = object.selectedItem - 1
  3024.             if object.selectedItem < 1 then
  3025.                 object.selectedItem = 1
  3026.             end
  3027.  
  3028.             if object.selectedItem == object.fromItem - 1 then
  3029.                 autoCompleteScroll(application, object, -1)
  3030.             end
  3031.  
  3032.             application:draw()
  3033.         elseif e4 == 208 then
  3034.             object.selectedItem = object.selectedItem + 1
  3035.             if object.selectedItem > object.itemCount then
  3036.                 object.selectedItem = object.itemCount
  3037.             end
  3038.  
  3039.             if object.selectedItem == object.fromItem + object.height then
  3040.                 autoCompleteScroll(application, object, 1)
  3041.             end
  3042.            
  3043.             application:draw()
  3044.         end
  3045.     end
  3046. end
  3047.  
  3048. local function autoCompleteClear(object)
  3049.     object.items = {}
  3050.     object.itemCount = 0
  3051.     object.fromItem = 1
  3052.     object.selectedItem = 1
  3053.     object.height = 0
  3054. end
  3055.  
  3056. local function autoCompleteMatch(object, variants, text, asKey)
  3057.     object:clear()
  3058.    
  3059.     if asKey then
  3060.         if text then
  3061.             for key in pairs(variants) do
  3062.                 if key ~= text and key:match("^" .. text) then
  3063.                     table.insert(object.items,key)
  3064.                 end
  3065.             end
  3066.         else
  3067.             for key in pairs(variants) do
  3068.                 table.insert(object.items, key)
  3069.             end
  3070.         end
  3071.     else
  3072.         if text then
  3073.             for i = 1, #variants do
  3074.                 if variants[i] ~= text and variants[i]:match("^" .. text) then
  3075.                     table.insert(object.items, variants[i])
  3076.                 end
  3077.             end
  3078.         else
  3079.             for i = 1, #variants do
  3080.                 table.insert(object.items, variants[i])
  3081.             end
  3082.         end
  3083.     end
  3084.  
  3085.     object.matchText = text or ""
  3086.     object.matchTextLength = unicode.len(object.matchText)
  3087.  
  3088.     table.sort(object.items, function(a, b) return unicode.lower(a) < unicode.lower(b) end)
  3089.  
  3090.     object.itemCount = #object.items
  3091.     object.height = math.min(object.itemCount, object.maximumHeight)
  3092.  
  3093.     return object
  3094. end
  3095.  
  3096. function GUI.autoComplete(x, y, width, maximumHeight, backgroundColor, textColor, textMatchColor, backgroundSelectedColor, textSelectedColor, textMatchSelectedColor, scrollBarBackground, scrollBarForeground)
  3097.     local object = GUI.object(x, y, width, maximumHeight)
  3098.  
  3099.     object.colors = {
  3100.         default = {
  3101.             background = backgroundColor,
  3102.             text = textColor,
  3103.             textMatch = textMatchColor 
  3104.         },
  3105.         selected = {
  3106.             background = backgroundSelectedColor,
  3107.             text = textSelectedColor,
  3108.             textMatch = textMatchSelectedColor
  3109.         }
  3110.     }
  3111.  
  3112.     object.maximumHeight = maximumHeight
  3113.     object.fromItem = 1
  3114.     object.selectedItem = 1
  3115.     object.items = {}
  3116.     object.matchText = " "
  3117.     object.matchTextLength = 1
  3118.     object.itemCount = 0
  3119.  
  3120.     object.scrollBar = GUI.scrollBar(1, 1, 1, 1, scrollBarBackground, scrollBarForeground, 1, 1, 1, 1, 1, true)
  3121.  
  3122.     object.match = autoCompleteMatch
  3123.     object.draw = autoCompleteDraw
  3124.     object.eventHandler = autoCompleteEventHandler
  3125.     object.clear = autoCompleteClear
  3126.  
  3127.     object:clear()
  3128.  
  3129.     return object
  3130. end
  3131.  
  3132. --------------------------------------------------------------------------------
  3133.  
  3134. local function brailleCanvasDraw(brailleCanvas)
  3135.     local index, background, foreground, symbol
  3136.     for y = 1, brailleCanvas.height do
  3137.         for x = 1, brailleCanvas.width do
  3138.             index = buffer.getIndex(brailleCanvas.x + x - 1, brailleCanvas.y + y - 1)
  3139.             background, foreground, symbol = buffer.rawGet(index)
  3140.             buffer.rawSet(index, background, brailleCanvas.pixels[y][x][9], brailleCanvas.pixels[y][x][10])
  3141.         end
  3142.     end
  3143.  
  3144.     return brailleCanvas
  3145. end
  3146.  
  3147. local function brailleCanvasSet(brailleCanvas, x, y, state, color)
  3148.     local xReal, yReal = math.ceil(x / 2), math.ceil(y / 4)
  3149.    
  3150.     brailleCanvas.pixels[yReal][xReal][(y - (yReal - 1) * 4 - 1) * 2 + x - (xReal - 1) * 2] = state and 1 or 0
  3151.     brailleCanvas.pixels[yReal][xReal][9] = color or brailleCanvas.pixels[yReal][xReal][9]
  3152.     brailleCanvas.pixels[yReal][xReal][10] = unicode.char(
  3153.         10240 +
  3154.         128 * brailleCanvas.pixels[yReal][xReal][8] +
  3155.         64 * brailleCanvas.pixels[yReal][xReal][7] +
  3156.         32 * brailleCanvas.pixels[yReal][xReal][6] +
  3157.         16 * brailleCanvas.pixels[yReal][xReal][4] +
  3158.         8 * brailleCanvas.pixels[yReal][xReal][2] +
  3159.         4 * brailleCanvas.pixels[yReal][xReal][5] +
  3160.         2 * brailleCanvas.pixels[yReal][xReal][3] +
  3161.         brailleCanvas.pixels[yReal][xReal][1]
  3162.     )
  3163.  
  3164.     return brailleCanvas
  3165. end
  3166.  
  3167. local function brailleCanvasGet(brailleCanvas, x, y)
  3168.     local xReal, yReal = math.ceil(x / 2), math.ceil(y / 4)
  3169.     return brailleCanvas.pixels[yReal][xReal][(y - (yReal - 1) * 4 - 1) * 2 + x - (xReal - 1) * 2], brailleCanvas.pixels[yReal][xReal][9], brailleCanvas.pixels[yReal][xReal][10]
  3170. end
  3171.  
  3172. local function brailleCanvasFill(brailleCanvas, x, y, width, height, state, color)
  3173.     for j = y, y + height - 1 do
  3174.         for i = x, x + width - 1 do
  3175.             brailleCanvas:set(i, j, state, color)
  3176.         end
  3177.     end
  3178. end
  3179.  
  3180. local function brailleCanvasClear(brailleCanvas)
  3181.     for j = 1, brailleCanvas.height * 4 do
  3182.         brailleCanvas.pixels[j] = {}
  3183.         for i = 1, brailleCanvas.width * 2 do
  3184.             brailleCanvas.pixels[j][i] = { 0, 0, 0, 0, 0, 0, 0, 0, 0x0, " " }
  3185.         end
  3186.     end
  3187. end
  3188.  
  3189. function GUI.brailleCanvas(x, y, width, height)
  3190.     local brailleCanvas = GUI.object(x, y, width, height)
  3191.    
  3192.     brailleCanvas.pixels = {}
  3193.  
  3194.     brailleCanvas.get = brailleCanvasGet
  3195.     brailleCanvas.set = brailleCanvasSet
  3196.     brailleCanvas.fill = brailleCanvasFill
  3197.     brailleCanvas.clear = brailleCanvasClear
  3198.  
  3199.     brailleCanvas.draw = brailleCanvasDraw
  3200.  
  3201.     brailleCanvas:clear()
  3202.  
  3203.     return brailleCanvas
  3204. end
  3205.  
  3206. --------------------------------------------------------------------------------
  3207.  
  3208. local function paletteShow(palette)
  3209.     local application = GUI.application()
  3210.    
  3211.     application:addChild(palette)
  3212.  
  3213.     palette.submitButton.onTouch = function()
  3214.         application:stop()
  3215.     end
  3216.     palette.cancelButton.onTouch = palette.submitButton.onTouch
  3217.  
  3218.     application:draw()
  3219.     application:start()
  3220.  
  3221.     return palette.color.integer
  3222. end
  3223.  
  3224. function GUI.palette(x, y, startColor)
  3225.     local palette = GUI.window(x, y, 71, 25)
  3226.    
  3227.     palette.color = {hsb = {}, rgb = {}}
  3228.     palette:addChild(GUI.panel(1, 1, palette.width, palette.height, 0xF0F0F0))
  3229.    
  3230.     local bigImage = palette:addChild(GUI.image(1, 1, image.create(50, 25)))
  3231.     local bigCrest = palette:addChild(GUI.object(1, 1, 5, 3))
  3232.  
  3233.     local function paletteDrawBigCrestPixel(x, y, symbol)
  3234.         local background, foreground = buffer.get(x, y)
  3235.         local r, g, b = color.integerToRGB(background)
  3236.         buffer.set(x, y, background, (r + g + b) / 3 >= 127 and 0x0 or 0xFFFFFF, symbol)
  3237.     end
  3238.  
  3239.     bigCrest.draw = function(object)
  3240.         paletteDrawBigCrestPixel(object.x, object.y + 1, "─")
  3241.         paletteDrawBigCrestPixel(object.x + 1, object.y + 1, "─")
  3242.         paletteDrawBigCrestPixel(object.x + 3, object.y + 1, "─")
  3243.         paletteDrawBigCrestPixel(object.x + 4, object.y + 1, "─")
  3244.         paletteDrawBigCrestPixel(object.x + 2, object.y, "│")
  3245.         paletteDrawBigCrestPixel(object.x + 2, object.y + 2, "│")
  3246.     end
  3247.     bigCrest.passScreenEvents = true
  3248.    
  3249.     local miniImage = palette:addChild(GUI.image(53, 1, image.create(3, 25)))
  3250.    
  3251.     local miniCrest = palette:addChild(GUI.object(52, 1, 5, 1))
  3252.     miniCrest.draw = function(object)
  3253.         buffer.drawText(object.x, object.y, 0x0, ">")
  3254.         buffer.drawText(object.x + 4, object.y, 0x0, "<")
  3255.     end
  3256.  
  3257.     local colorPanel = palette:addChild(GUI.panel(58, 2, 12, 3, 0x0))
  3258.     palette.submitButton = palette:addChild(GUI.roundedButton(58, 6, 12, 1, 0x4B4B4B, 0xFFFFFF, 0x2D2D2D, 0xFFFFFF, "OK"))
  3259.     palette.cancelButton = palette:addChild(GUI.roundedButton(58, 8, 12, 1, 0xFFFFFF, 0x696969, 0x2D2D2D, 0xFFFFFF, "Cancel"))
  3260.  
  3261.     local function paletteRefreshBigImage()
  3262.         local saturationStep, brightnessStep, saturation, brightness = 1 / bigImage.width, 1 / bigImage.height, 0, 1
  3263.         for j = 1, bigImage.height do
  3264.             for i = 1, bigImage.width do
  3265.                 image.set(bigImage.image, i, j, color.optimize(color.HSBToInteger(palette.color.hsb.hue, saturation, brightness)), 0x0, 0x0, " ")
  3266.                 saturation = saturation + saturationStep
  3267.             end
  3268.             saturation, brightness = 0, brightness - brightnessStep
  3269.         end
  3270.     end
  3271.  
  3272.     local function paletteRefreshMiniImage()
  3273.         local hueStep, hue = 360 / miniImage.height, 0
  3274.         for j = 1, miniImage.height do
  3275.             for i = 1, miniImage.width do
  3276.                 image.set(miniImage.image, i, j, color.optimize(color.HSBToInteger(hue, 1, 1)), 0x0, 0, " ")
  3277.             end
  3278.             hue = hue + hueStep
  3279.         end
  3280.     end
  3281.  
  3282.     local function paletteUpdateCrestsCoordinates()
  3283.         bigCrest.localX = math.floor((bigImage.width - 1) * palette.color.hsb.saturation) - 1
  3284.         bigCrest.localY = math.floor((bigImage.height - 1) - (bigImage.height - 1) * palette.color.hsb.brightness)
  3285.         miniCrest.localY = math.ceil(palette.color.hsb.hue / 360 * miniImage.height + 0.5)
  3286.     end
  3287.  
  3288.     local inputs
  3289.  
  3290.     local function paletteUpdateInputs()
  3291.         inputs[1].text = tostring(palette.color.rgb.red)
  3292.         inputs[2].text = tostring(palette.color.rgb.green)
  3293.         inputs[3].text = tostring(palette.color.rgb.blue)
  3294.         inputs[4].text = tostring(math.floor(palette.color.hsb.hue))
  3295.         inputs[5].text = tostring(math.floor(palette.color.hsb.saturation * 100))
  3296.         inputs[6].text = tostring(math.floor(palette.color.hsb.brightness * 100))
  3297.         inputs[7].text = string.format("%06X", palette.color.integer)
  3298.         colorPanel.colors.background = palette.color.integer
  3299.     end
  3300.  
  3301.     local function paletteSwitchColorFromHex(hex)
  3302.         palette.color.integer = hex
  3303.         palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue = color.integerToRGB(hex)
  3304.         palette.color.hsb.hue, palette.color.hsb.saturation, palette.color.hsb.brightness = color.RGBToHSB(palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue)
  3305.         paletteUpdateInputs()
  3306.     end
  3307.  
  3308.     local function paletteSwitchColorFromHsb(hue, saturation, brightness)
  3309.         palette.color.hsb.hue, palette.color.hsb.saturation, palette.color.hsb.brightness = hue, saturation, brightness
  3310.         palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue = color.HSBToRGB(hue, saturation, brightness)
  3311.         palette.color.integer = color.RGBToInteger(palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue)
  3312.         paletteUpdateInputs()
  3313.     end
  3314.  
  3315.     local function paletteSwitchColorFromRgb(red, green, blue)
  3316.         palette.color.rgb.red, palette.color.rgb.green, palette.color.rgb.blue = red, green, blue
  3317.         palette.color.hsb.hue, palette.color.hsb.saturation, palette.color.hsb.brightness = color.RGBToHSB(red, green, blue)
  3318.         palette.color.integer = color.RGBToInteger(red, green, blue)
  3319.         paletteUpdateInputs()
  3320.     end
  3321.  
  3322.     local function onAnyInputFinished()
  3323.         paletteRefreshBigImage()
  3324.         paletteUpdateCrestsCoordinates()
  3325.         palette.firstParent:draw()
  3326.     end
  3327.  
  3328.     local function onHexInputFinished()
  3329.         paletteSwitchColorFromHex(tonumber("0x" .. inputs[7].text))
  3330.         onAnyInputFinished()
  3331.     end
  3332.  
  3333.     local function onRgbInputFinished()
  3334.         paletteSwitchColorFromRgb(tonumber(inputs[1].text), tonumber(inputs[2].text), tonumber(inputs[3].text))
  3335.         onAnyInputFinished()
  3336.     end
  3337.  
  3338.     local function onHsbInputFinished()
  3339.         paletteSwitchColorFromHsb(tonumber(inputs[4].text), tonumber(inputs[5].text) / 100, tonumber(inputs[6].text) / 100)
  3340.         onAnyInputFinished()
  3341.     end
  3342.  
  3343.     local function rgbValidaror(text)
  3344.         local number = tonumber(text) if number and number >= 0 and number <= 255 then return true end
  3345.     end
  3346.  
  3347.     local function hValidator(text)
  3348.         local number = tonumber(text) if number and number >= 0 and number <= 359 then return true end
  3349.     end
  3350.  
  3351.     local function sbValidator(text)
  3352.         local number = tonumber(text) if number and number >= 0 and number <= 100 then return true end
  3353.     end
  3354.  
  3355.     local function hexValidator(text)
  3356.         if string.match(text, "^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$") then
  3357.             return true
  3358.         end
  3359.     end
  3360.  
  3361.     inputs = {
  3362.         { shortcut = "R:", validator = rgbValidaror, onInputFinished = onRgbInputFinished },
  3363.         { shortcut = "G:", validator = rgbValidaror, onInputFinished = onRgbInputFinished },
  3364.         { shortcut = "B:", validator = rgbValidaror, onInputFinished = onRgbInputFinished },
  3365.         { shortcut = "H:", validator = hValidator,   onInputFinished = onHsbInputFinished },
  3366.         { shortcut = "S:", validator = sbValidator,  onInputFinished = onHsbInputFinished },
  3367.         { shortcut = "L:", validator = sbValidator,  onInputFinished = onHsbInputFinished },
  3368.         { shortcut = "0x", validator = hexValidator, onInputFinished = onHexInputFinished }
  3369.     }
  3370.  
  3371.     local y = 10
  3372.     for i = 1, #inputs do
  3373.         palette:addChild(GUI.label(58, y, 2, 1, 0x0, inputs[i].shortcut))
  3374.        
  3375.         local validator, onInputFinished = inputs[i].validator, inputs[i].onInputFinished
  3376.         inputs[i] = palette:addChild(GUI.input(61, y, 9, 1, 0xFFFFFF, 0x696969, 0x696969, 0xFFFFFF, 0x0, "", "", true))
  3377.         inputs[i].validator = validator
  3378.         inputs[i].onInputFinished = onInputFinished
  3379.        
  3380.         y = y + 2
  3381.     end
  3382.    
  3383.     local favourites
  3384.     if filesystem.exists(GUI.PALETTE_CONFIG_PATH) then
  3385.         favourites = table.fromFile(GUI.PALETTE_CONFIG_PATH)
  3386.     else
  3387.         favourites = {}
  3388.         for i = 1, 6 do favourites[i] = color.HSBToInteger(math.random(0, 360), 1, 1) end
  3389.         table.toFile(GUI.PALETTE_CONFIG_PATH, favourites)
  3390.     end
  3391.  
  3392.     local favouritesContainer = palette:addChild(GUI.container(58, 24, 12, 1))
  3393.     for i = 1, #favourites do
  3394.         favouritesContainer:addChild(GUI.button(i * 2 - 1, 1, 2, 1, favourites[i], 0x0, 0x0, 0x0, " ")).onTouch = function(application)
  3395.             paletteSwitchColorFromHex(favourites[i])
  3396.             paletteRefreshBigImage()
  3397.             paletteUpdateCrestsCoordinates()
  3398.             application:draw()
  3399.         end
  3400.     end
  3401.    
  3402.     palette:addChild(GUI.button(58, 25, 12, 1, 0xFFFFFF, 0x4B4B4B, 0x2D2D2D, 0xFFFFFF, "+")).onTouch = function(application)
  3403.         local favouriteExists = false
  3404.         for i = 1, #favourites do
  3405.             if favourites[i] == palette.color.integer then
  3406.                 favouriteExists = true
  3407.                 break
  3408.             end
  3409.         end
  3410.        
  3411.         if not favouriteExists then
  3412.             table.insert(favourites, 1, palette.color.integer)
  3413.             table.remove(favourites, #favourites)
  3414.             for i = 1, #favourites do
  3415.                 favouritesContainer.children[i].colors.default.background = favourites[i]
  3416.                 favouritesContainer.children[i].colors.pressed.background = 0x0
  3417.             end
  3418.            
  3419.             table.toFile(GUI.PALETTE_CONFIG_PATH, favourites)
  3420.  
  3421.             application:draw()
  3422.         end
  3423.     end
  3424.  
  3425.     bigImage.eventHandler = function(application, object, e1, e2, e3, e4)
  3426.         if e1 == "touch" or e1 == "drag" then
  3427.             bigCrest.localX, bigCrest.localY = e3 - palette.x - 1, e4 - palette.y
  3428.             paletteSwitchColorFromHex(select(3, component.gpu.get(e3, e4)))
  3429.             application:draw()
  3430.         end
  3431.     end
  3432.    
  3433.     miniImage.eventHandler = function(application, object, e1, e2, e3, e4)
  3434.         if e1 == "touch" or e1 == "drag" then
  3435.             miniCrest.localY = e4 - palette.y + 1
  3436.             paletteSwitchColorFromHsb((e4 - miniImage.y) * 360 / miniImage.height, palette.color.hsb.saturation, palette.color.hsb.brightness)
  3437.             paletteRefreshBigImage()
  3438.             application:draw()
  3439.         end
  3440.     end
  3441.  
  3442.     palette.show = paletteShow
  3443.  
  3444.     paletteSwitchColorFromHex(startColor)
  3445.     paletteUpdateCrestsCoordinates()
  3446.     paletteRefreshBigImage()
  3447.     paletteRefreshMiniImage()
  3448.  
  3449.     return palette
  3450. end
  3451.  
  3452. --------------------------------------------------------------------------------
  3453.  
  3454. local function textUpdate(object)
  3455.     object.width = unicode.len(object.text)
  3456.     return object
  3457. end
  3458.  
  3459. local function textDraw(object)
  3460.     object:update()
  3461.     buffer.drawText(object.x, object.y, object.color, object.text)
  3462.     return object
  3463. end
  3464.  
  3465. function GUI.text(x, y, color, text)
  3466.     local object = GUI.object(x, y, 1, 1)
  3467.  
  3468.     object.text = text
  3469.     object.color = color
  3470.     object.update = textUpdate
  3471.     object.draw = textDraw
  3472.     object:update()
  3473.  
  3474.     return object
  3475. end
  3476.  
  3477. --------------------------------------------------------------------------------
  3478.  
  3479. function GUI.addBackgroundContainer(parentContainer, addPanel, addLayout, title)
  3480.     local container = parentContainer:addChild(GUI.container(1, 1, parentContainer.width, parentContainer.height))
  3481.    
  3482.     if addPanel then
  3483.         container.panel = container:addChild(GUI.panel(1, 1, container.width, container.height, GUI.BACKGROUND_CONTAINER_PANEL_COLOR, GUI.BACKGROUND_CONTAINER_PANEL_TRANSPARENCY))
  3484.         container.panel.eventHandler = function(parentContainer, object, e1)
  3485.             if e1 == "touch" then
  3486.                 container:remove()
  3487.                 parentContainer:draw()
  3488.             end
  3489.         end
  3490.     end
  3491.  
  3492.     if addLayout then
  3493.         container.layout = container:addChild(GUI.layout(1, 1, container.width, container.height, 1, 1))
  3494.  
  3495.         if title then
  3496.             container.label = container.layout:addChild(GUI.label(1, 1, 1, 1, GUI.BACKGROUND_CONTAINER_TITLE_COLOR, title)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
  3497.         end
  3498.     end
  3499.  
  3500.     return container
  3501. end
  3502.  
  3503. --------------------------------------------------------------------------------
  3504.  
  3505. local function listUpdate(list)
  3506.     local step, child = false
  3507.     for i = 1, #list.children do
  3508.         child = list.children[i]
  3509.         -- Жмяканье пизды
  3510.         child.pressed = i == list.selectedItem
  3511.        
  3512.         -- Цвет залупы
  3513.         if step then
  3514.             child.colors.default = list.colors.alternating
  3515.         else
  3516.             child.colors.default = list.colors.default
  3517.         end
  3518.         child.colors.pressed, step = list.colors.selected, not step
  3519.        
  3520.         -- Размеры хуйни
  3521.         if list.cells[1][1].direction == GUI.DIRECTION_HORIZONTAL then
  3522.             if list.offsetMode then
  3523.                 child.width, child.height = list.itemSize * 2 + unicode.len(child.text), list.height
  3524.             else
  3525.                 child.width, child.height = list.itemSize, list.height
  3526.             end
  3527.         else
  3528.             if list.offsetMode then
  3529.                 child.width, child.height = list.width, list.itemSize * 2 + 1
  3530.             else
  3531.                 child.width, child.height = list.width, list.itemSize
  3532.             end
  3533.         end
  3534.     end
  3535.  
  3536.     layoutUpdate(list)
  3537. end
  3538.  
  3539. local function listItemEventHandler(application, item, e1, ...)
  3540.     if e1 == "touch" or e1 == "drag" then
  3541.         item.parent.selectedItem = item:indexOf()
  3542.         item.parent:update()
  3543.         application:draw()
  3544.  
  3545.         if item.onTouch then
  3546.             item.onTouch(application, item, e1, ...)
  3547.         end
  3548.     end
  3549. end
  3550.  
  3551. local function listAddItem(list, text)
  3552.     local item = list:addChild(pressable(1, 1, 1, 1, 0, 0, 0, 0, 0, 0, text))
  3553.    
  3554.     item.switchMode = true
  3555.     item.eventHandler = listItemEventHandler
  3556.  
  3557.     return item
  3558. end
  3559.  
  3560. local function listSetAlignment(list, ...)
  3561.     layoutSetAlignment(list, 1, 1, ...)
  3562.     return list
  3563. end
  3564.  
  3565. local function listSetSpacing(list, ...)
  3566.     layoutSetSpacing(list, 1, 1, ...)
  3567.     return list
  3568. end
  3569.  
  3570. local function listSetDirection(list, ...)
  3571.     layoutSetDirection(list, 1, 1, ...)
  3572.     return list
  3573. end
  3574.  
  3575. local function listSetFitting(list, ...)
  3576.     layoutSetFitting(list, 1, 1, ...)
  3577.     return list
  3578. end
  3579.  
  3580. local function listSetMargin(list, ...)
  3581.     layoutSetMargin(list, 1, 1, ...)
  3582.     return list
  3583. end
  3584.  
  3585. local function listGetMargin(list, ...)
  3586.     return layoutGetMargin(list, 1, 1, ...)
  3587. end
  3588.  
  3589. local function listGetItem(list, what)
  3590.     if type(what) == "number" then
  3591.         return list.children[what]
  3592.     else
  3593.         for i = 1, #list.children do
  3594.             if list.children[i].text == what then
  3595.                 return list.children[i], i
  3596.             end
  3597.         end
  3598.     end
  3599. end
  3600.  
  3601. local function listCount(list)
  3602.     return #list.children
  3603. end
  3604.  
  3605. local function listDraw(list)
  3606.     buffer.drawRectangle(list.x, list.y, list.width, list.height, list.colors.default.background, list.colors.default.text, " ")
  3607.     layoutDraw(list)
  3608. end
  3609.  
  3610. function GUI.list(x, y, width, height, itemSize, spacing, backgroundColor, textColor, backgroundAlternatingColor, textAlternatingColor, backgroundSelectedColor, textSelectedColor, offsetMode)
  3611.     local list = GUI.layout(x, y, width, height, 1, 1)
  3612.  
  3613.     list.colors = {
  3614.         default = {
  3615.             background = backgroundColor,
  3616.             text = textColor
  3617.         },
  3618.         alternating = {
  3619.             background = backgroundAlternatingColor,
  3620.             text = textAlternatingColor
  3621.         },
  3622.         selected = {
  3623.             background = backgroundSelectedColor,
  3624.             text = textSelectedColor
  3625.         },
  3626.     }
  3627.  
  3628.     list.passScreenEvents = false
  3629.     list.selectedItem = 1
  3630.     list.offsetMode = offsetMode
  3631.     list.itemSize = itemSize
  3632.    
  3633.     list.addItem = listAddItem
  3634.     list.getItem = listGetItem
  3635.     list.count = listCount
  3636.     list.setAlignment = listSetAlignment
  3637.     list.setSpacing = listSetSpacing
  3638.     list.setDirection = listSetDirection
  3639.     list.setFitting = listSetFitting
  3640.     list.setMargin = listSetMargin
  3641.     list.getMargin = listGetMargin
  3642.     list.update = listUpdate
  3643.     list.draw = listDraw
  3644.  
  3645.     list:setAlignment(GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
  3646.     list:setSpacing(spacing)
  3647.     list:setDirection(GUI.DIRECTION_VERTICAL)
  3648.  
  3649.     return list
  3650. end
  3651.  
  3652. ---------------------------------------------------------------------------------------------------
  3653.  
  3654. local function keyAndValueUpdate(object)
  3655.     object.keyLength, object.valueLength = unicode.len(object.key), unicode.len(object.value)
  3656.     object.width = object.keyLength + object.valueLength
  3657. end
  3658.  
  3659. local function keyAndValueDraw(object)
  3660.     keyAndValueUpdate(object)
  3661.     buffer.drawText(object.x, object.y, object.colors.key, object.key)
  3662.     buffer.drawText(object.x + object.keyLength, object.y, object.colors.value, object.value)
  3663. end
  3664.  
  3665. function GUI.keyAndValue(x, y, keyColor, valueColor, key, value)
  3666.     local object = GUI.object(x, y, 1, 1)
  3667.    
  3668.     object.colors = {
  3669.         key = keyColor,
  3670.         value = valueColor
  3671.     }
  3672.     object.key = key
  3673.     object.value = value
  3674.  
  3675.     object.update = keyAndValueUpdate
  3676.     object.draw = keyAndValueDraw
  3677.  
  3678.     object:update()
  3679.  
  3680.     return object
  3681. end
  3682.  
  3683. ---------------------------------------------------------------------------------------------------
  3684.  
  3685. function GUI.highlightString(x, y, width, fromChar, indentationWidth, patterns, colorScheme, s)
  3686.     fromChar = fromChar or 1
  3687.    
  3688.     local counter, symbols, colors, stringLength, bufferIndex, newFrameBackgrounds, newFrameForegrounds, newFrameSymbols, searchFrom, starting, ending = indentationWidth, {}, {}, unicode.len(s), buffer.getIndex(x, y), buffer.getNewFrameTables()
  3689.     local toChar = math.min(stringLength, fromChar + width - 1)
  3690.  
  3691.     -- Пидорасим на символы
  3692.     for i = fromChar, toChar do
  3693.         symbols[i] = unicode.sub(s, i, i)
  3694.     end
  3695.  
  3696.     -- Вгоняем в цветовую карту синтаксическую подсветку
  3697.     for j = 1, #patterns, 4 do
  3698.         searchFrom = 1
  3699.        
  3700.         while true do
  3701.             starting, ending = string.unicodeFind(s, patterns[j], searchFrom)
  3702.            
  3703.             if starting then
  3704.                 for i = starting + patterns[j + 2], ending - patterns[j + 3] do
  3705.                     colors[i] = colorScheme[patterns[j + 1]]
  3706.                 end
  3707.             else
  3708.                 break
  3709.             end
  3710.  
  3711.             searchFrom = ending + 1 - patterns[j + 3]
  3712.         end
  3713.     end
  3714.  
  3715.     -- Ебошим индентейшны
  3716.     for i = fromChar, toChar do
  3717.         if symbols[i] == " " then
  3718.             colors[i] = colorScheme.indentation
  3719.            
  3720.             if counter == indentationWidth then
  3721.                 symbols[i], counter = "│", 0
  3722.             end
  3723.  
  3724.             counter = counter + 1
  3725.         else
  3726.             break
  3727.         end
  3728.     end
  3729.  
  3730.     -- Рисуем текст
  3731.     for i = fromChar, toChar do
  3732.         newFrameForegrounds[bufferIndex], newFrameSymbols[bufferIndex] = colors[i] or colorScheme.text, symbols[i] or " "
  3733.         bufferIndex = bufferIndex + 1
  3734.     end
  3735. end
  3736.  
  3737. --------------------------------------------------------------------------------
  3738.  
  3739. local function dropDownMenuItemDraw(item)
  3740.     local yText = item.y + math.floor(item.height / 2)
  3741.  
  3742.     if item.type == 1 then
  3743.         local textColor = item.color or item.parent.parent.colors.default.text
  3744.  
  3745.         if item.pressed then
  3746.             textColor = item.parent.parent.colors.selected.text
  3747.             buffer.drawRectangle(item.x, item.y, item.width, item.height, item.parent.parent.colors.selected.background, textColor, " ")
  3748.         elseif item.disabled then
  3749.             textColor = item.parent.parent.colors.disabled.text
  3750.         end
  3751.  
  3752.         buffer.drawText(item.x + 1, yText, textColor, item.text)
  3753.         if item.shortcut then
  3754.             buffer.drawText(item.x + item.width - unicode.len(item.shortcut) - 1, yText, textColor, item.shortcut)
  3755.         end
  3756.     else
  3757.         buffer.drawText(item.x, yText, item.parent.parent.colors.separator, string.rep("─", item.width))
  3758.     end
  3759.  
  3760.     return item
  3761. end
  3762.  
  3763. local function dropDownMenuReleaseItems(menu)
  3764.     for i = 1, #menu.itemsContainer.children do
  3765.         menu.itemsContainer.children[i].pressed = false
  3766.     end
  3767.  
  3768.     return menu
  3769. end
  3770.  
  3771. local function dropDownMenuItemEventHandler(application, object, e1, ...)
  3772.     if e1 == "touch" then
  3773.         if object.type == 1 and not object.pressed then
  3774.             object.pressed = true
  3775.             application:draw()
  3776.  
  3777.             if object.subMenu then
  3778.                 object.parent.parent.parent:addChild(object.subMenu:releaseItems())
  3779.                 object.subMenu.localX = object.parent.parent.localX + object.parent.parent.width
  3780.                 object.subMenu.localY = object.parent.parent.localY + object.localY - 1
  3781.                 if buffer.getWidth() - object.parent.parent.localX - object.parent.parent.width + 1 < object.subMenu.width then
  3782.                     object.subMenu.localX = object.parent.parent.localX - object.subMenu.width
  3783.                     object.parent.parent:moveToFront()
  3784.                 end
  3785.  
  3786.                 application:draw()
  3787.             else
  3788.                 os.sleep(0.2)
  3789.  
  3790.                 object.parent.parent.parent:remove()
  3791.                
  3792.                 local objectIndex = object:indexOf()
  3793.                 for i = 2, #object.parent.parent.parent.children do
  3794.                     if object.parent.parent.parent.children[i].onMenuClosed then
  3795.                         object.parent.parent.parent.children[i].onMenuClosed(objectIndex)
  3796.                     end
  3797.                 end
  3798.  
  3799.                 if object.onTouch then
  3800.                     object.onTouch()
  3801.                 end
  3802.  
  3803.                 application:draw()
  3804.             end
  3805.         end
  3806.     end
  3807. end
  3808.  
  3809. local function dropDownMenuGetHeight(menu)
  3810.     local height = 0
  3811.     for i = 1, #menu.itemsContainer.children do
  3812.         height = height + (menu.itemsContainer.children[i].type == 2 and 1 or menu.itemHeight)
  3813.     end
  3814.  
  3815.     return height
  3816. end
  3817.  
  3818. local function dropDownMenuReposition(menu)
  3819.     menu.itemsContainer.width, menu.itemsContainer.height = menu.width, menu.height
  3820.     menu.prevButton.width, menu.nextButton.width = menu.width, menu.width
  3821.     menu.nextButton.localY = menu.height
  3822.  
  3823.     local y = menu.itemsContainer.children[1].localY
  3824.     for i = 1, #menu.itemsContainer.children do
  3825.         menu.itemsContainer.children[i].localY = y
  3826.         menu.itemsContainer.children[i].width = menu.itemsContainer.width
  3827.         y = y + menu.itemsContainer.children[i].height
  3828.     end
  3829.  
  3830.     menu.prevButton.hidden = menu.itemsContainer.children[1].localY >= 1
  3831.     menu.nextButton.hidden = menu.itemsContainer.children[#menu.itemsContainer.children].localY + menu.itemsContainer.children[#menu.itemsContainer.children].height - 1 <= menu.height
  3832. end
  3833.  
  3834. local function dropDownMenuUpdate(menu)
  3835.     if #menu.itemsContainer.children > 0 then
  3836.         menu.height = math.min(dropDownMenuGetHeight(menu), menu.maximumHeight, buffer.getHeight() - menu.y)
  3837.         dropDownMenuReposition(menu)
  3838.     end
  3839. end
  3840.  
  3841. local function dropDownMenuRemoveItem(menu, index)
  3842.     table.remove(menu.itemsContainer.children, index)
  3843.  
  3844.     menu:update()
  3845.  
  3846.     return menu
  3847. end
  3848.  
  3849. local function dropDownMenuAddItem(menu, text, disabled, shortcut, color)
  3850.     local item = menu.itemsContainer:addChild(GUI.object(1, 1, 1, menu.itemHeight))
  3851.     item.type = 1
  3852.     item.text = text
  3853.     item.disabled = disabled
  3854.     item.shortcut = shortcut
  3855.     item.color = color
  3856.     item.draw = dropDownMenuItemDraw
  3857.     item.eventHandler = dropDownMenuItemEventHandler
  3858.  
  3859.     menu:update()
  3860.  
  3861.     return item
  3862. end
  3863.  
  3864. local function dropDownMenuAddSeparator(menu)
  3865.     local item = menu.itemsContainer:addChild(GUI.object(1, 1, 1, 1))
  3866.     item.type = 2
  3867.     item.draw = dropDownMenuItemDraw
  3868.     item.eventHandler = dropDownMenuItemEventHandler
  3869.  
  3870.     menu:update()
  3871.  
  3872.     return item
  3873. end
  3874.  
  3875. local function dropDownMenuScrollDown(application, menu)
  3876.     if menu.itemsContainer.children[1].localY < 1 then
  3877.         for i = 1, #menu.itemsContainer.children do
  3878.             menu.itemsContainer.children[i].localY = menu.itemsContainer.children[i].localY + 1
  3879.         end
  3880.  
  3881.         dropDownMenuReposition(menu)
  3882.         application:draw()
  3883.     end
  3884. end
  3885.  
  3886. local function dropDownMenuScrollUp(application, menu)
  3887.     if menu.itemsContainer.children[#menu.itemsContainer.children].localY + menu.itemsContainer.children[#menu.itemsContainer.children].height - 1 > menu.height then
  3888.         for i = 1, #menu.itemsContainer.children do
  3889.             menu.itemsContainer.children[i].localY = menu.itemsContainer.children[i].localY - 1
  3890.         end
  3891.  
  3892.         dropDownMenuReposition(menu)
  3893.         application:draw()
  3894.     end
  3895. end
  3896.  
  3897. local function dropDownMenuEventHandler(application, menu, e1, e2, e3, e4, e5)
  3898.     if e1 == "scroll" then
  3899.         if e5 == 1 then
  3900.             dropDownMenuScrollDown(application, menu)
  3901.         else
  3902.             dropDownMenuScrollUp(application, menu)
  3903.         end
  3904.     end
  3905. end
  3906.  
  3907. local function dropDownMenuPrevButtonOnTouch(application, button)
  3908.     dropDownMenuScrollDown(application, button.parent)
  3909. end
  3910.  
  3911. local function dropDownMenuNextButtonOnTouch(application, button)
  3912.     dropDownMenuScrollUp(application, button.parent)
  3913. end
  3914.  
  3915. local function dropDownMenuDraw(menu)
  3916.     buffer.drawRectangle(menu.x, menu.y, menu.width, menu.height, menu.colors.default.background, menu.colors.default.text, " ", menu.colors.transparency.background)
  3917.     GUI.drawShadow(menu.x, menu.y, menu.width, menu.height, menu.colors.transparency.shadow, true)
  3918.     containerDraw(menu)
  3919. end
  3920.  
  3921. local function dropDownMenuBackgroundObjectEventHandler(application, object, e1)
  3922.     if e1 == "touch" then
  3923.         for i = 2, #object.parent.children do
  3924.             if object.parent.children[i].onMenuClosed then
  3925.                 object.parent.children[i].onMenuClosed()
  3926.             end
  3927.         end
  3928.  
  3929.         object.parent:remove()
  3930.         application:draw()
  3931.     end
  3932. end
  3933.  
  3934. local function dropDownMenuAdd(parentContainer, menu)
  3935.     local container = parentContainer:addChild(GUI.container(1, 1, parentContainer.width, parentContainer.height))
  3936.     container:addChild(GUI.object(1, 1, container.width, container.height)).eventHandler = dropDownMenuBackgroundObjectEventHandler
  3937.    
  3938.     return container:addChild(menu:releaseItems())
  3939. end
  3940.  
  3941. function GUI.dropDownMenu(x, y, width, maximumHeight, itemHeight, backgroundColor, textColor, backgroundPressedColor, textPressedColor, disabledColor, separatorColor, backgroundTransparency, shadowTransparency)
  3942.     local menu = GUI.container(x, y, width, 1)
  3943.    
  3944.     menu.colors = {
  3945.         default = {
  3946.             background = backgroundColor,
  3947.             text = textColor
  3948.         },
  3949.         selected = {
  3950.             background = backgroundPressedColor,
  3951.             text = textPressedColor
  3952.         },
  3953.         disabled = {
  3954.             text = disabledColor
  3955.         },
  3956.         separator = separatorColor,
  3957.         transparency = {
  3958.             background = backgroundTransparency,
  3959.             shadow = shadowTransparency
  3960.         }
  3961.     }
  3962.  
  3963.     menu.itemsContainer = menu:addChild(GUI.container(1, 1, menu.width, menu.height))
  3964.     menu.prevButton = menu:addChild(GUI.button(1, 1, menu.width, 1, backgroundColor, textColor, backgroundPressedColor, textPressedColor, "▲"))
  3965.     menu.nextButton = menu:addChild(GUI.button(1, 1, menu.width, 1, backgroundColor, textColor, backgroundPressedColor, textPressedColor, "▼"))
  3966.     menu.prevButton.colors.transparency, menu.nextButton.colors.transparency = backgroundTransparency, backgroundTransparency
  3967.     menu.prevButton.onTouch = dropDownMenuPrevButtonOnTouch
  3968.     menu.nextButton.onTouch = dropDownMenuNextButtonOnTouch
  3969.  
  3970.     menu.releaseItems = dropDownMenuReleaseItems
  3971.     menu.itemHeight = itemHeight
  3972.     menu.addSeparator = dropDownMenuAddSeparator
  3973.     menu.addItem = dropDownMenuAddItem
  3974.     menu.removeItem = dropDownMenuRemoveItem
  3975.     menu.draw = dropDownMenuDraw
  3976.     menu.maximumHeight = maximumHeight
  3977.     menu.eventHandler = dropDownMenuEventHandler
  3978.     menu.update = dropDownMenuUpdate
  3979.  
  3980.     return menu
  3981. end
  3982.  
  3983. --------------------------------------------------------------------------------
  3984.  
  3985. local function contextMenuUpdate(menu)
  3986.     if #menu.itemsContainer.children > 0 then
  3987.         local widestItem, widestShortcut = 0, 0
  3988.         for i = 1, #menu.itemsContainer.children do
  3989.             if menu.itemsContainer.children[i].type == 1 then
  3990.                 widestItem = math.max(widestItem, unicode.len(menu.itemsContainer.children[i].text))
  3991.                 if menu.itemsContainer.children[i].shortcut then
  3992.                     widestShortcut = math.max(widestShortcut, unicode.len(menu.itemsContainer.children[i].shortcut))
  3993.                 end
  3994.             end
  3995.         end
  3996.  
  3997.         menu.width, menu.height = 2 + widestItem + (widestShortcut > 0 and 3 + widestShortcut or 0), math.min(dropDownMenuGetHeight(menu), menu.maximumHeight)
  3998.         dropDownMenuReposition(menu)
  3999.  
  4000.         local bufferWidth, bufferHeight = buffer.getResolution()
  4001.         if menu.x + menu.width + 1 >= bufferWidth then
  4002.             menu.localX = bufferWidth - menu.width - 1
  4003.         end
  4004.         if menu.y + menu.height >= bufferHeight then
  4005.             menu.localY = bufferHeight - menu.height
  4006.         end
  4007.     end
  4008. end
  4009.  
  4010. local contextMenuCreate, contextMenuAddSubMenu
  4011.  
  4012. contextMenuAddSubMenu = function(menu, text, disabled)
  4013.     local item = menu:addItem(text, disabled, "►")
  4014.     item.subMenu = contextMenuCreate(1, 1)
  4015.     item.subMenu.colors = menu.colors
  4016.    
  4017.     return item.subMenu
  4018. end
  4019.  
  4020. contextMenuCreate = function(x, y, backgroundColor, textColor, backgroundPressedColor, textPressedColor, disabledColor, separatorColor, backgroundTransparency, shadowTransparency)
  4021.     local menu = GUI.dropDownMenu(
  4022.         x,
  4023.         y,
  4024.         1,
  4025.         math.ceil(buffer.getHeight() * 0.5),
  4026.         1,
  4027.         backgroundColor or GUI.CONTEXT_MENU_DEFAULT_BACKGROUND_COLOR,
  4028.         textColor or GUI.CONTEXT_MENU_DEFAULT_TEXT_COLOR,
  4029.         backgroundPressedColor or GUI.CONTEXT_MENU_PRESSED_BACKGROUND_COLOR,
  4030.         textPressedColor or GUI.CONTEXT_MENU_PRESSED_TEXT_COLOR,
  4031.         disabledColor or GUI.CONTEXT_MENU_DISABLED_COLOR,
  4032.         separatorColor or GUI.CONTEXT_MENU_SEPARATOR_COLOR,
  4033.         backgroundTransparency or GUI.CONTEXT_MENU_BACKGROUND_TRANSPARENCY,
  4034.         shadowTransparency or GUI.CONTEXT_MENU_SHADOW_TRANSPARENCY
  4035.     )
  4036.  
  4037.     menu.update = contextMenuUpdate
  4038.     menu.addSubMenu = contextMenuAddSubMenu
  4039.  
  4040.     return menu
  4041. end
  4042.  
  4043. function GUI.addContextMenu(parentContainer, arg1, ...)
  4044.     if type(arg1) == "table" then
  4045.         return dropDownMenuAdd(parentContainer, arg1, ...)
  4046.     else
  4047.         return dropDownMenuAdd(parentContainer, contextMenuCreate(arg1, ...))
  4048.     end
  4049. end
  4050.  
  4051. --------------------------------------------------------------------------------
  4052.  
  4053. local function comboBoxDraw(object)
  4054.     buffer.drawRectangle(object.x, object.y, object.width, object.height, object.colors.default.background, object.colors.default.text, " ")
  4055.     if object.dropDownMenu.itemsContainer.children[object.selectedItem] then
  4056.         buffer.drawText(object.x + 1, math.floor(object.y + object.height / 2), object.colors.default.text, string.limit(object.dropDownMenu.itemsContainer.children[object.selectedItem].text, object.width - object.height - 2, "right"))
  4057.     end
  4058.  
  4059.     local width = object.height * 2 - 1
  4060.     buffer.drawRectangle(object.x + object.width - object.height * 2 + 1, object.y, width, object.height, object.colors.arrow.background, object.colors.arrow.text, " ")
  4061.     buffer.drawText(math.floor(object.x + object.width - width / 2), math.floor(object.y + object.height / 2), object.colors.arrow.text, object.pressed and "▲" or "▼")
  4062.  
  4063.     return object
  4064. end
  4065.  
  4066. local function comboBoxGetItem(object, what)
  4067.     if type(what) == "number" then
  4068.         return object.dropDownMenu.itemsContainer.children[what]
  4069.     else
  4070.         local children = object.dropDownMenu.itemsContainer.children
  4071.         for i = 1, #children do
  4072.             if children[i].text == what then
  4073.                 return children[i], i
  4074.             end
  4075.         end
  4076.     end
  4077. end
  4078.  
  4079. local function comboBoxRemoveItem(object, index)
  4080.     object.dropDownMenu:removeItem(index)
  4081.     if object.selectedItem > #object.dropDownMenu.itemsContainer.children then
  4082.         object.selectedItem = #object.dropDownMenu.itemsContainer.children
  4083.     end
  4084. end
  4085.  
  4086. local function comboBoxCount(object)
  4087.     return #object.dropDownMenu.itemsContainer.children
  4088. end
  4089.  
  4090. local function comboBoxClear(object)
  4091.     object.dropDownMenu.itemsContainer:removeChildren()
  4092.     object.selectedItem = 1
  4093.  
  4094.     return object
  4095. end
  4096.  
  4097. local function comboBoxEventHandler(application, object, e1, ...)
  4098.     if e1 == "touch" and #object.dropDownMenu.itemsContainer.children > 0 then
  4099.         object.pressed = true
  4100.         object.dropDownMenu.x, object.dropDownMenu.y, object.dropDownMenu.width = object.x, object.y + object.height, object.width
  4101.         object.dropDownMenu:update()
  4102.         dropDownMenuAdd(application, object.dropDownMenu)
  4103.         application:draw()
  4104.     end
  4105. end
  4106.  
  4107. local function comboBoxAddItem(object, ...)
  4108.     return object.dropDownMenu:addItem(...)
  4109. end
  4110.  
  4111. local function comboBoxAddSeparator(object)
  4112.     return object.dropDownMenu:addSeparator()
  4113. end
  4114.  
  4115. function GUI.comboBox(x, y, width, itemSize, backgroundColor, textColor, arrowBackgroundColor, arrowTextColor)
  4116.     local comboBox = GUI.object(x, y, width, itemSize)
  4117.    
  4118.     comboBox.colors = {
  4119.         default = {
  4120.             background = backgroundColor,
  4121.             text = textColor
  4122.         },
  4123.         selected = {
  4124.             background = GUI.CONTEXT_MENU_PRESSED_BACKGROUND_COLOR,
  4125.             text = GUI.CONTEXT_MENU_PRESSED_TEXT_COLOR
  4126.         },
  4127.         arrow = {
  4128.             background = arrowBackgroundColor,
  4129.             text = arrowTextColor
  4130.         }
  4131.     }
  4132.  
  4133.     comboBox.dropDownMenu = GUI.dropDownMenu(
  4134.         1,
  4135.         1,
  4136.         1,
  4137.         math.ceil(buffer.getHeight() * 0.5),
  4138.         itemSize,
  4139.         comboBox.colors.default.background,
  4140.         comboBox.colors.default.text,
  4141.         comboBox.colors.selected.background,
  4142.         comboBox.colors.selected.text,
  4143.         GUI.CONTEXT_MENU_DISABLED_COLOR,
  4144.         GUI.CONTEXT_MENU_SEPARATOR_COLOR,
  4145.         GUI.CONTEXT_MENU_BACKGROUND_TRANSPARENCY,
  4146.         GUI.CONTEXT_MENU_SHADOW_TRANSPARENCY
  4147.     )
  4148.  
  4149.     comboBox.dropDownMenu.onMenuClosed = function(index)
  4150.         comboBox.pressed = false
  4151.         comboBox.selectedItem = index or comboBox.selectedItem
  4152.         comboBox.firstParent:draw()
  4153.        
  4154.         if index and comboBox.onItemSelected then
  4155.             comboBox.onItemSelected(index)
  4156.         end
  4157.     end
  4158.  
  4159.     comboBox.selectedItem = 1
  4160.     comboBox.addItem = comboBoxAddItem
  4161.     comboBox.removeItem = comboBoxRemoveItem
  4162.     comboBox.addSeparator = comboBoxAddSeparator
  4163.     comboBox.draw = comboBoxDraw
  4164.     comboBox.clear = comboBoxClear
  4165.     comboBox.getItem = comboBoxGetItem
  4166.     comboBox.count = comboBoxCount
  4167.     comboBox.eventHandler = comboBoxEventHandler
  4168.  
  4169.     return comboBox
  4170. end
  4171.  
  4172. ---------------------------------------------------------------------------------------------------
  4173.  
  4174. function windowDraw(window)
  4175.     containerDraw(window)
  4176.     GUI.drawShadow(window.x, window.y, window.width, window.height, GUI.WINDOW_SHADOW_TRANSPARENCY, true)
  4177.  
  4178.     return window
  4179. end
  4180.  
  4181. local function windowCheck(window, x, y)
  4182.     local child
  4183.     for i = #window.children, 1, -1 do
  4184.         child = window.children[i]
  4185.        
  4186.         if
  4187.             not child.hidden and
  4188.             not child.disabled and
  4189.             x >= child.x and
  4190.             x < child.x + child.width and
  4191.             y >= child.y and
  4192.             y < child.y + child.height
  4193.         then
  4194.             if not child.passScreenEvents and child.eventHandler then
  4195.                 return true
  4196.             elseif child.children then
  4197.                 local result = windowCheck(child, x, y)
  4198.                 if result == true then
  4199.                     return true
  4200.                 elseif result == false then
  4201.                     return false
  4202.                 end
  4203.             end
  4204.         end
  4205.     end
  4206. end
  4207.  
  4208. local function windowEventHandler(application, window, e1, e2, e3, e4, ...)
  4209.     if e1 == "touch" then
  4210.         if not windowCheck(window, e3, e4) then
  4211.             window.lastTouchX, window.lastTouchY = e3, e4
  4212.         end
  4213.  
  4214.         if window ~= window.parent.children[#window.parent.children] then
  4215.             window:moveToFront()
  4216.            
  4217.             if window.onFocus then
  4218.                 window.onFocus(application, window, e1, e2, e3, e4, ...)
  4219.             end
  4220.  
  4221.             application:draw()
  4222.         end
  4223.     elseif e1 == "drag" and window.lastTouchX and not windowCheck(window, e3, e4) then
  4224.         local xOffset, yOffset = e3 - window.lastTouchX, e4 - window.lastTouchY
  4225.         if xOffset ~= 0 or yOffset ~= 0 then
  4226.             window.localX, window.localY = window.localX + xOffset, window.localY + yOffset
  4227.             window.lastTouchX, window.lastTouchY = e3, e4
  4228.            
  4229.             application:draw()
  4230.         end
  4231.     elseif e1 == "drop" then
  4232.         window.lastTouchX, window.lastTouchY = nil, nil
  4233.     end
  4234. end
  4235.  
  4236. local function windowResize(window, width, height)
  4237.     window.width, window.height = width, height
  4238.     if window.onResize then
  4239.         window.onResize(width, height)
  4240.     end
  4241.  
  4242.     return window
  4243. end
  4244.  
  4245. local function windowMaximize(window)
  4246.     if window.maximized then
  4247.         window.localX, window.localY = window.oldGeometryX, window.oldGeometryY
  4248.         window:resize(window.oldGeometryWidth, window.oldGeometryHeight)
  4249.     else
  4250.         window.oldGeometryX, window.oldGeometryY, window.oldGeometryWidth, window.oldGeometryHeight = window.localX, window.localY, window.width, window.height
  4251.         window.localX, window.localY = 1, 1
  4252.         window:resize(window.parent.width, window.parent.height)
  4253.     end
  4254.  
  4255.     window.maximized = not window.maximized
  4256.     window.firstParent:draw()
  4257. end
  4258.  
  4259. local function windowMinimize(window)
  4260.     window.hidden = not window.hidden
  4261.     window.firstParent:draw()
  4262. end
  4263.  
  4264. local function windowClose(window)
  4265.     window:remove()
  4266.     window.firstParent:draw()
  4267. end
  4268.  
  4269. function GUI.window(x, y, width, height)
  4270.     local window = GUI.container(x, y, width, height)
  4271.    
  4272.     window.passScreenEvents = false
  4273.  
  4274.     window.resize = windowResize
  4275.     window.maximize = windowMaximize
  4276.     window.minimize = windowMinimize
  4277.     window.close = windowClose
  4278.     window.eventHandler = windowEventHandler
  4279.     window.draw = windowDraw
  4280.  
  4281.     return window
  4282. end
  4283.  
  4284. function GUI.filledWindow(x, y, width, height, backgroundColor)
  4285.     local window = GUI.window(x, y, width, height)
  4286.  
  4287.     window.backgroundPanel = window:addChild(GUI.panel(1, 1, width, height, backgroundColor))
  4288.     window.actionButtons = window:addChild(GUI.actionButtons(2, 2, true))
  4289.  
  4290.     return window
  4291. end
  4292.  
  4293. function GUI.titledWindow(x, y, width, height, title, addTitlePanel)
  4294.     local window = GUI.filledWindow(x, y, width, height, GUI.WINDOW_BACKGROUND_PANEL_COLOR)
  4295.  
  4296.     if addTitlePanel then
  4297.         window.titlePanel = window:addChild(GUI.panel(1, 1, width, 1, GUI.WINDOW_TITLE_BACKGROUND_COLOR))
  4298.         window.backgroundPanel.localY, window.backgroundPanel.height = 2, window.height - 1
  4299.     end
  4300.  
  4301.     window.titleLabel = window:addChild(GUI.label(1, 1, width, height, GUI.WINDOW_TITLE_TEXT_COLOR, title)):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
  4302.     window.actionButtons.localY = 1
  4303.     window.actionButtons:moveToFront()
  4304.  
  4305.     return window
  4306. end
  4307.  
  4308. function GUI.tabbedWindow(x, y, width, height, ...)
  4309.     local window = GUI.filledWindow(x, y, width, height, GUI.WINDOW_BACKGROUND_PANEL_COLOR)
  4310.  
  4311.     window.tabBar = window:addChild(GUI.tabBar(1, 1, window.width, 3, 2, 0, GUI.WINDOW_TAB_BAR_DEFAULT_BACKGROUND_COLOR, GUI.WINDOW_TAB_BAR_DEFAULT_TEXT_COLOR, GUI.WINDOW_TAB_BAR_DEFAULT_BACKGROUND_COLOR, GUI.WINDOW_TAB_BAR_DEFAULT_TEXT_COLOR, GUI.WINDOW_TAB_BAR_SELECTED_BACKGROUND_COLOR, GUI.WINDOW_TAB_BAR_SELECTED_TEXT_COLOR, true))
  4312.    
  4313.     window.backgroundPanel.localY, window.backgroundPanel.height = 4, window.height - 3
  4314.     window.actionButtons:moveToFront()
  4315.     window.actionButtons.localY = 2
  4316.  
  4317.     return window
  4318. end
  4319.  
  4320. ---------------------------------------------------------------------------------------------------
  4321.  
  4322. function GUI.tabBar(...)
  4323.     local tabBar = GUI.list(...)
  4324.  
  4325.     tabBar:setDirection(GUI.DIRECTION_HORIZONTAL)
  4326.     tabBar:setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
  4327.  
  4328.     return tabBar
  4329. end
  4330.  
  4331. --------------------------------------------------------------------------------
  4332.  
  4333. local function menuDraw(menu)
  4334.     buffer.drawRectangle(menu.x, menu.y, menu.width, 1, menu.colors.default.background, menu.colors.default.text, " ", menu.colors.transparency)
  4335.     layoutDraw(menu)
  4336. end
  4337.  
  4338. local function menuAddItem(menu, text, textColor)
  4339.     local item = menu:addChild(pressable(1, 1, unicode.len(text) + 2, 1, nil, textColor or menu.colors.default.text, menu.colors.selected.background, menu.colors.selected.text, 0x0, 0x0, text))
  4340.     item.eventHandler = pressableEventHandler
  4341.  
  4342.     return item
  4343. end
  4344.  
  4345. local function menuGetItem(menu, what)
  4346.     if type(what) == "number" then
  4347.         return menu.children[what]
  4348.     else
  4349.         for i = 1, #menu.children do
  4350.             if menu.children[i].text == what then
  4351.                 return menu.children[i], i
  4352.             end
  4353.         end
  4354.     end
  4355. end
  4356.  
  4357. local function menuContextMenuItemOnTouch(application, item)
  4358.     item.contextMenu.x, item.contextMenu.y = item.x, item.y + 1
  4359.     dropDownMenuAdd(application, item.contextMenu)
  4360.  
  4361.     application:draw()
  4362. end
  4363.  
  4364. local function menuAddContextMenu(menu, ...)
  4365.     local item = menu:addItem(...)
  4366.  
  4367.     item.switchMode = true
  4368.     item.onTouch = menuContextMenuItemOnTouch
  4369.     item.contextMenu = contextMenuCreate(1, 1)
  4370.     item.contextMenu.onMenuClosed = function()
  4371.         item.pressed = false
  4372.         item.firstParent:draw()
  4373.     end
  4374.  
  4375.     return item.contextMenu
  4376. end
  4377.  
  4378. function GUI.menu(x, y, width, backgroundColor, textColor, backgroundPressedColor, textPressedColor, backgroundTransparency)
  4379.     local menu = GUI.layout(x, y, width, 1, 1, 1)
  4380.    
  4381.     menu.colors = {
  4382.         default = {
  4383.             background = backgroundColor,
  4384.             text = textColor,
  4385.         },
  4386.         selected = {
  4387.             background = backgroundPressedColor,
  4388.             text = textPressedColor,
  4389.         },
  4390.         transparency = backgroundTransparency
  4391.     }
  4392.    
  4393.     menu.passScreenEvents = false
  4394.     menu.addContextMenu = menuAddContextMenu
  4395.     menu.addItem = menuAddItem
  4396.     menu.getItem = menuGetItem
  4397.     menu.draw = menuDraw
  4398.  
  4399.     menu:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
  4400.     menu:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
  4401.     menu:setSpacing(1, 1, 0)
  4402.     menu:setMargin(1, 1, 1, 0)
  4403.  
  4404.     return menu
  4405. end
  4406.  
  4407. ---------------------------------------------------------------------------------------------------
  4408.  
  4409. local function progressIndicatorDraw(self)
  4410.     local color = self.active and (self.position == 1 and self.colors.secondary or self.colors.primary) or self.colors.passive
  4411.     buffer.drawText(self.x + 1, self.y, color, "⢀")
  4412.     buffer.drawText(self.x + 2, self.y, color, "⡀")
  4413.  
  4414.     color = self.active and (self.position == 2 and self.colors.secondary or self.colors.primary) or self.colors.passive
  4415.     buffer.drawText(self.x + 3, self.y + 1, color, "⠆")
  4416.     buffer.drawText(self.x + 2, self.y + 1, color, "⢈")
  4417.  
  4418.     color = self.active and (self.position == 3 and self.colors.secondary or self.colors.primary) or self.colors.passive
  4419.     buffer.drawText(self.x + 1, self.y + 2, color, "⠈")
  4420.     buffer.drawText(self.x + 2, self.y + 2, color, "⠁")
  4421.  
  4422.     color = self.active and (self.position == 4 and self.colors.secondary or self.colors.primary) or self.colors.passive
  4423.     buffer.drawText(self.x, self.y + 1, color, "⠰")
  4424.     buffer.drawText(self.x + 1, self.y + 1, color, "⡁")
  4425. end
  4426.  
  4427. local function progressIndicatorRoll(self)
  4428.     self.position = self.position + 1
  4429.     if self.position > 4 then
  4430.         self.position = 1
  4431.     end
  4432. end
  4433.  
  4434. local function progressIndicatorReset(self, state)
  4435.     self.active = state
  4436.     self.position = 1
  4437. end
  4438.  
  4439. function GUI.progressIndicator(x, y, passiveColor, primaryColor, secondaryColor)
  4440.     local object = GUI.object(x, y, 4, 3)
  4441.    
  4442.     object.colors = {
  4443.         passive = passiveColor,
  4444.         primary = primaryColor,
  4445.         secondary = secondaryColor
  4446.     }
  4447.  
  4448.     object.active = false
  4449.     object.reset = progressIndicatorReset
  4450.     object.draw = progressIndicatorDraw
  4451.     object.roll = progressIndicatorRoll
  4452.  
  4453.     object:reset()
  4454.  
  4455.     return object
  4456. end
  4457.  
  4458. ---------------------------------------------------------------------------------------------------
  4459.  
  4460. return GUI
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top