vitalik228

[GUI] - IgorTimofeev

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