Advertisement
HawkPB

OpenUI

Aug 1st, 2024 (edited)
404
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 21.04 KB | None | 0 0
  1. -- OpenUI --
  2. -- Developed by Hawk --
  3. -- discord: hwkg --
  4.  
  5. -- theres a big manual at the bottom for things you might wanna know if you wanna create your own ui library
  6.  
  7. --/REQUIRES/--
  8. local component = require "component"
  9. local gpu = component.gpu
  10. local event = require "event"
  11. local shell = require "shell"
  12. local unicode = require "unicode" -- used only for the length function (string.len isnt good for cool utf-8 characters)
  13. local fs = require "filesystem"
  14.  
  15. local w, h = gpu.getResolution()
  16.  
  17. -- contains openui
  18. local API = {}
  19.  
  20. -- contains all the ui elements made by openui, you can access it with openui.getUIElements()
  21. local uiElements = {}
  22.  
  23. -- list of used colors generated by chatgpt, you can access them with openui.colors
  24. local colors = {
  25.     red = 0xFF0000,
  26.     green = 0x00FF00,
  27.     blue = 0x0000FF,
  28.     white = 0xFFFFFF,
  29.     black = 0x000000,
  30.     yellow = 0xFFFF00,
  31.     cyan = 0x00FFFF,
  32.     magenta = 0xFF00FF,
  33.     gray = 0x808080,
  34.     darkGray = 0x404040,
  35.     lightGray = 0xD3D3D3,
  36.     orange = 0xFFA500,
  37.     darkOrange = 0xFF8C00,
  38.     purple = 0x800080,
  39.     pink = 0xFFC0CB,
  40.     darkPink = 0xFF1493,
  41.     brown = 0xA52A2A,
  42.     darkBrown = 0x654321,
  43.     olive = 0x808000,
  44.     darkOlive = 0x556B2F,
  45.     teal = 0x008080,
  46.     darkTeal = 0x004C4C,
  47.     navy = 0x000080,
  48.     maroon = 0x800000,
  49.     silver = 0xC0C0C0,
  50.     lime = 0x00FF00,
  51.     darkLime = 0x32CD32,
  52.     gold = 0xFFD700,
  53.     darkGold = 0xB8860B,
  54.     coral = 0xFF7F50,
  55.     darkCoral = 0xCD5B45,
  56.     beige = 0xF5F5DC,
  57.     darkBeige = 0xD2B48C,
  58.     lavender = 0xE6E6FA,
  59.     darkLavender = 0x7C4C7D,
  60.     turquoise = 0x40E0D0,
  61.     darkTurquoise = 0x00CED1,
  62.     indigo = 0x4B0082,
  63.     darkIndigo = 0x2E0854,
  64.     violet = 0xEE82EE,
  65.     darkViolet = 0x9400D3,
  66.     salmon = 0xFA8072,
  67.     darkSalmon = 0xE9967A,
  68.     khaki = 0xF0E68C,
  69.     darkKhaki = 0xBDB76B,
  70.     plum = 0xDDA0DD,
  71.     darkPlum = 0x8B008B,
  72.     tan = 0xD2B48C,
  73.     darkTan = 0x918151,
  74.     mint = 0xF5FFFA,
  75.     darkMint = 0x98FF98,
  76.     charcoal = 0x36454F,
  77.     darkCharcoal = 0x2F4F4F,
  78.     sienna = 0xA0522D,
  79.     darkSienna = 0x5D3A1A,
  80.     lightBlue = 0xADD8E6,
  81.     darkBlue = 0x00008B,
  82.     lightGreen = 0x90EE90,
  83.     darkGreen = 0x006400,
  84.     lightRed = 0xFF6666,
  85.     darkRed = 0x8B0000,
  86.     lightPurple = 0x9370DB,
  87.     darkPurple = 0x301934,
  88.     lightYellow = 0xFFFFE0,
  89.     darkYellow = 0x9B870C
  90. }
  91.  
  92. -- TODO -- : make the interface and stop using pastebin
  93.  
  94. -- a prototype to a interface ill make later
  95. local args, ops = shell.parse(...)
  96. if (ops.u) then
  97.     print("attempting download...")
  98.     if (args[1]) then
  99.         if (fs.exists(fs.concat(shell.getWorkingDirectory(), args[1]))) then
  100.             print("downloading latest version to " .. fs.concat(shell.getWorkingDirectory(), args[1]))
  101.             shell.execute("pastebin get 2dBBnQH6 " .. fs.concat(shell.getWorkingDirectory(), args[1]) .. " -f")
  102.         else
  103.             print("no file or folder exists there")
  104.         end
  105.     else
  106.         print("please provide a filename to download to, ex: openui -u openui.lua")
  107.     end
  108. end
  109. function setbg(c, b)
  110.     gpu.setBackground(c, b)
  111. end
  112. function setfg(c, b)
  113.     gpu.setForeground(c, b)
  114. end
  115.  
  116. --/TEMPLATES/--
  117. API.templates = {
  118.     --make a exit button at the top right of hte screen
  119.     exitButton = function()
  120.         local newButton = addButton(w, 2, 3, 1) -- make a button at the far right of the screen, with 3 chars in width and 1 in height
  121.         newButton.style.color = colors.red -- make the color red (red in hex)
  122.         newButton.text = "X"
  123.         newButton.style.textColor = colors.black -- make the text black
  124.         newButton.onClick = function()
  125.             -- this function is fired by openui (MUST CALL openui.uiEventHandlers()) when the button  is pressed
  126.             os.exit() -- close the program if the buttons clicked
  127.         end
  128.         return newButton
  129.     end
  130. }
  131.  
  132. -- its literally just a simple rectangle with barely any customizability
  133. function API.addSimpleRect(x1, y1, x2, y2)
  134.     local newRect = {}
  135.     newRect.ElementCheck = true
  136.     newRect.type = "Simple Rect"
  137.     newRect.x1 = x1
  138.     newRect.y1 = y1
  139.     newRect.x2 = x2
  140.     newRect.y2 = y2
  141.     newRect.interactable = false
  142.     newRect.hidden = false
  143.     newRect.style = {
  144.         color = colors.green
  145.     }
  146.     table.insert(uiElements, newRect)
  147.     return newRect
  148. end
  149.  
  150. -- this is a rectangle with customizability for characters of corners and edges
  151. function API.addAdvRect(x1, y1, x2, y2)
  152.     local newRect = {}
  153.     newRect.ElementCheck = true
  154.     newRect.type = "Advanced Rect"
  155.     newRect.x1 = x1
  156.     newRect.y1 = y1
  157.     newRect.x2 = x2
  158.     newRect.y2 = y2
  159.     newRect.interactable = false
  160.     newRect.hidden = false
  161.     newRect.style = {
  162.         color = colors.green,
  163.         backgroundColor = colors.black,
  164.         TopLeftCornerChar = "┌",
  165.         LeftEdgeChar = "│",
  166.         BottomLeftCornerChar = "└",
  167.         BottomEdgeChar = "─",
  168.         BottomRightCornerChar = "┙",
  169.         RightEdgeChar = "│",
  170.         TopRightCornerChar = "┐",
  171.         TopEdgeChar = "─"
  172.     }
  173.     table.insert(uiElements, newRect)
  174.     return newRect
  175. end
  176.  
  177. -- this uses bresenhams and draws a series of points to form a line, customizable character
  178. function API.addLine(x1, y1, x2, y2)
  179.     local newLine = {}
  180.     newLine.ElementCheck = true
  181.     newLine.type = "Line"
  182.     newLine.x1 = x1
  183.     newLine.y1 = y1
  184.     newLine.x2 = x2
  185.     newLine.y2 = y2
  186.     newLine.interactable = false
  187.     newLine.hidden = false
  188.     newLine.style = {
  189.         color = colors.green,
  190.         char = "█"
  191.     }
  192.     table.insert(uiElements, newLine)
  193.     return newWire
  194. end
  195.  
  196. -- TODO -- : Add more styles to this, fix the AnchorPoint
  197. -- this just displays text
  198. function addLabel(x, y, text)
  199.     local newLabel = {}
  200.     newLabel.ElementCheck = true
  201.     newLabel.type = "Label"
  202.     newLabel.text = text
  203.     newLabel.x = x
  204.     newLabel.y = y
  205.     newLabel.interactable = false
  206.     newLabel.hidden = false
  207.     newLabel.style = {
  208.         id = 1,
  209.         color = colors.white,
  210.         AnchorPoint = 1, -- 1=center, 2=far left, 3=far right
  211.         backgroundColor = colors.darkGreen
  212.     }
  213.     table.insert(uiElements, newLabel)
  214.     return newLabel
  215. end
  216. API.addLabel = addLabel -- so it can be used in the api
  217.  
  218. -- puts a char at where that x and y is
  219. function addMarker(x, y)
  220.     local newMarker = {}
  221.     newMarker.ElementCheck = true
  222.     newMarker.type = "Marker"
  223.     newMarker.x = x
  224.     newMarker.y = y
  225.     newMarker.interactable = false
  226.     newMarker.hidden = false
  227.     newMarker.style = {
  228.         color = colors.yellow,
  229.         backgroundColor = colors.black,
  230.         char = "#"
  231.     }
  232.     table.insert(uiElements, newMarker)
  233.     return newMarker
  234. end
  235. API.addMarker = addMarker -- so it can be used in the api
  236.  
  237. -- TODO -- :  fix the hitboxes
  238. -- its a button, NOTE: MUST CALL openui.uiEventHandlers() IF YOU WANT TO USE THE onClick() EVENT
  239. function addButton(x, y, w, h)
  240.     local newButton = {}
  241.     newButton.ElementCheck = true -- a simple check to see if its a openui element
  242.     newButton.type = "Button" -- a type for what ui element it is
  243.     newButton.x = x
  244.     newButton.y = y
  245.     newButton.width = w
  246.     newButton.height = h
  247.     newButton.text = ""
  248.     newButton.onClick = function()
  249.         -- fires when its pressed NOTE: MUST CALL openui.uiEventHandlers()
  250.     end
  251.     newButton.hidden = false
  252.     newButton.interactable = true
  253.     newButton.style = {
  254.         id = 1, -- style id, 0=custom, 1=default 2=background
  255.         color = colors.green, -- default green
  256.         textColor = colors.white,
  257.         layerBackgroundColor = colors.darkGreen, -- layer is the layer behind the button in most styles
  258.         layerColor = colors.white,
  259.         AnchorPoint = 1 -- 1=center, 2=top left
  260.     }
  261.     table.insert(uiElements, newButton)
  262.     return newButton
  263. end
  264. API.addButton = addButton -- so it can be used in the api
  265.  
  266. -- TODO -- : make this not a single character, add more styles
  267. -- a single character button with builtin toggle functionallity  NOTE: MUST CALL openui.uiEventHandlers()
  268. function addToggleButton(x, y)
  269.     local newToggleButton = {}
  270.     newToggleButton.ElementCheck = true
  271.     newToggleButton.type = "Toggle Button"
  272.     newToggleButton.x = x
  273.     newToggleButton.y = y
  274.     newToggleButton.width = 1
  275.     newToggleButton.height = 1
  276.     newToggleButton.toggle = false
  277.     newToggleButton.onToggle = function(bool) -- NOTE: MUST CALL openui.uiEventHandlers()
  278.     end
  279.     newToggleButton.interactable = true
  280.     newToggleButton.hidden = false
  281.     newToggleButton.style = {
  282.         id = 1
  283.     }
  284.     table.insert(uiElements, newToggleButton)
  285.     return newToggleButton
  286. end
  287. API.addToggleButton = addToggleButton -- so it can be used in the api
  288.  
  289. -- wipes all ui elements, because they carry on and dont wipe when running different openui instances
  290. function API.clearUIElements()
  291.     uiElements = {}
  292. end
  293.  
  294. -- just put this here so you dont have to do `require"component".gpu.getResolution()[1]"
  295. function API.width()
  296.     return w
  297. end
  298. -- just put this here so you dont have to do `require"component".gpu.getResolution()[2]"
  299. function API.height()
  300.     return w
  301. end
  302.  
  303. -- returns the table containing all openui uiElements
  304. function API.getUIElements()
  305.     return uiElements
  306. end
  307.  
  308. -- resets the foreground and background and clears everything
  309. function API.clearScreen()
  310.     setbg(colors.black, false)
  311.     setfg(colors.white, false)
  312.     gpu.fill(1, 1, w, h, " ")
  313. end
  314.  
  315. API.colors = colors -- so you can access the colors
  316.  
  317. -- you gotta call this whenever something changes in the ui and you want to update it
  318. function API.draw()
  319.     for _, element in ipairs(uiElements) do -- iterate through all the ui elements
  320.         if (element.ElementCheck == true) then -- ElementCheck is just a simple check to see if its a openui object
  321.             if (not element.hidden) then -- if its hidden you dont wanna render it
  322.                 if (element.type == "Button") then
  323.                     setbg(element.style.color, false)
  324.                     local sx = element.x
  325.                     local sy = element.y
  326.                     if (element.style.AnchorPoint == 1) then -- logic for centering the button
  327.                         sx = element.x - element.width / 2
  328.                         sy = element.y - element.height / 2
  329.                     end
  330.                     if (element.style.id == 1) then -- simplest style
  331.                         gpu.fill(sx, sy, element.width, element.height, " ")
  332.                     elseif (element.style.id == 2) then -- just with a shadow
  333.                         setbg(element.style.layerBackgroundColor, false)
  334.                         setfg(element.style.layerColor, false)
  335.                         gpu.fill(sx + 1, sy + 1, element.width, element.height, "▓")
  336.                         setbg(element.style.color, false)
  337.                         gpu.fill(sx, sy, element.width, element.height, " ")
  338.                     elseif (element.style.id == 3) then -- shadow and curved outlines
  339.                         gpu.set(sx, sy, "╭")
  340.                         gpu.set(sx + element.width - 1, sy, "╮")
  341.                         gpu.set(sx, sy + element.height - 1, "╰")
  342.                         gpu.set(sx + element.width - 1, sy + element.height - 1, "╯")
  343.                         gpu.fill(sx + 1, sy, element.width - 2, 1, "─")
  344.                         gpu.fill(sx, sy + 1, 1, element.height - 2, "│")
  345.                         gpu.fill(sx + 1, sy + element.height - 1, element.width - 2, 1, "─")
  346.                         gpu.fill(sx + element.width - 1, sy + 1, 1, element.height - 2, "│")
  347.  
  348.                         setbg(element.style.layerBackgroundColor, false)
  349.                         setfg(element.style.layerColor, false)
  350.                         gpu.set(sx + 1, sy + element.height, "╰")
  351.                         gpu.fill(sx + 2, sy + element.height, element.width - 2, 1, "─")
  352.                         gpu.set(sx + element.width, sy + element.height, "╯")
  353.                         gpu.set(sx + element.width, sy + 1, "╮")
  354.                         gpu.fill(sx + element.width, sy + 2, 1, element.height - 2, "│")
  355.                     end
  356.                     setbg(element.style.color)
  357.                     setfg(element.style.textColor)
  358.                     if (element.text ~= "") then
  359.                         gpu.set( -- the math is for centering the text on the button
  360.                             sx + element.width / 2 - unicode.len(element.text) / 2, -- using unicode.len fixes the issue where box characters counted as 3 characters
  361.                             sy + element.height / 2 - 1,
  362.                             element.text
  363.                         )
  364.                     end
  365.                 elseif (element.type == "Line") then
  366.                     setbg(element.style.color)
  367.                     -- this uses bresenhams line algorithm to draw a line
  368.                     local x1_ = element.x1
  369.                     local x2_ = element.x2
  370.                     local y1_ = element.y1
  371.                     local y2_ = element.y2
  372.  
  373.                     local dx = math.abs(x2_ - x1_)
  374.                     local dy = math.abs(y2_ - y1_)
  375.                     local sx = x1_ < x2_ and 1 or -1
  376.                     local sy = y1_ < y2_ and 1 or -1
  377.                     local err = dx - dy
  378.                     while true do
  379.                         gpu.set(x1_, y1_, element.style.char)
  380.                         if x1_ == x2_ and y1_ == y2_ then
  381.                             break
  382.                         end
  383.                         local e2 = err * 2
  384.                         if e2 > -dy then
  385.                             err = err - dy
  386.                             x1_ = x1_ + sx
  387.                         end
  388.                         if e2 < dx then
  389.                             err = err + dx
  390.                             y1_ = y1_ + sy
  391.                         end
  392.                     end
  393.                 elseif (element.type == "Label") then
  394.                     setbg(element.style.backgroundColor)
  395.                     setfg(element.style.color)
  396.                     local sx = element.x
  397.                     local sy = element.y
  398.                     if (element.AnchorPoint == 1) then
  399.                         sx = element.x - unicode.len(element.text) / 2 -- centering it
  400.                     elseif (element.AnchorPoint == 3) then
  401.                         sx = element.x + unicode.len(element.text) / 2 -- snapping it on the right
  402.                     end
  403.                     gpu.set(sx, sy, element.text)
  404.                 elseif (element.type == "Marker") then
  405.                     setbg(element.style.backgroundColor)
  406.                     setfg(element.style.color)
  407.                     gpu.set(element.x, element.y, element.style.char)
  408.                 elseif (element.type == "Toggle Button") then
  409.                     if (element.style.id == 1) then
  410.                         if (element.toggle) then
  411.                             setfg(colors.green)
  412.                         else
  413.                             setfg(colors.red)
  414.                         end
  415.                         gpu.set(element.x, element.y, "◉")
  416.                     end
  417.                 elseif (element.type == "Simple Rect") then
  418.                     setbg(element.style.color)
  419.                     -- all the min and max is in case x1,y1 is greater than x2,y2 (gpu.fill doesnt like that)
  420.                     local minx = math.min(element.x1, element.x2)
  421.                     local miny = math.min(element.y1, element.y2)
  422.                     local maxx = math.max(element.x1, element.x2)
  423.                     local maxy = math.max(element.y1, element.y2)
  424.                     gpu.fill(minx, miny, maxx - minx, maxy - miny, " ")
  425.                 elseif (element.type == "Advanced Rect") then
  426.                     setbg(element.style.backgroundColor)
  427.                     setfg(element.style.color)
  428.                     -- all the min and max is in case x1,y1 is greater than x2,y2 (gpu.fill doesnt like that)
  429.                     local minx = math.min(element.x1, element.x2)
  430.                     local miny = math.min(element.y1, element.y2)
  431.                     local maxx = math.max(element.x1, element.x2)
  432.                     local maxy = math.max(element.y1, element.y2)
  433.                     gpu.set(minx, miny, element.style.TopLeftCornerChar)
  434.                     gpu.set(minx, maxy, element.style.BottomLeftCornerChar)
  435.                     gpu.set(maxx, maxy, element.style.BottomRightCornerChar)
  436.                     gpu.set(maxx, miny, element.style.TopRightCornerChar)
  437.                     gpu.fill(minx, miny + 1, 1, maxy - miny - 1, element.style.LeftEdgeChar)
  438.                     gpu.fill(minx + 1, miny, maxx - minx - 1, 1, element.style.TopEdgeChar)
  439.                     gpu.fill(maxx, miny + 1, 1, maxy - miny - 1, element.style.RightEdgeChar)
  440.                     gpu.fill(minx + 1, maxy, maxx - minx - 1, 1, element.style.BottomEdgeChar)
  441.                 end
  442.             end
  443.         end
  444.     end
  445.     -- reset the colors back to default
  446.     setbg(colors.black)
  447.     setfg(colors.white)
  448. end
  449. function API.uiEventHandlers()
  450.     local eventType, _, x, y = event.pull("touch") -- click events
  451.     if eventType == "touch" then
  452.         for _, element in ipairs(uiElements) do -- iterate through all the ui elements
  453.             if (element.interactable) then -- if its interactable
  454.                 if (not element.hidden) then -- if its not hidden
  455.                     if (element.type == "Button") then
  456.                         if (element.style.AnchorPoint == 1) then
  457.                             if
  458.                                 x >= math.floor(element.x - element.width / 2) and
  459.                                     x <= math.floor(element.x + element.width / 2) and
  460.                                     y >= math.floor(element.y - element.height / 2) and
  461.                                     y <= math.floor(element.y + element.height / 2)
  462.                              then
  463.                                 element.onClick()
  464.                             end
  465.                         else
  466.                             if
  467.                                 x >= element.x and x <= element.x + element.width - 1 and y >= element.y and
  468.                                     y <= element.y + element.height - 1
  469.                              then
  470.                                 element.onClick()
  471.                             end
  472.                         end
  473.                     end
  474.                     if (element.type == "Toggle Button") then
  475.                         if (x == element.x and y == element.y) then
  476.                             element.onToggle(not element.toggle)
  477.                             element.toggle = not element.toggle
  478.                         end
  479.                     end
  480.                 end
  481.             end
  482.         end
  483.     end
  484. end
  485.  
  486. --[[ big ui library manual
  487.  ───────────────────────────
  488.  ┌─GPU
  489.  ├─ gpu can be gotten by doing require("component").gpu
  490.  ├─ the docs to the gpu component are here: https://ocdoc.cil.li/component:gpu
  491.  └─┬SCREEN FUNCTIONS
  492.    ├─ to set a single char you would do gpu.set(x,y,char)
  493.    ├─ to set a string you would do gpu.set(x,y,string)
  494.    ├─┬COLOR
  495.    │ ├─ color works by setting the text color and background before setting the characters
  496.    │ ├─ the color system uses a hex system, ex: 0xFFFFFF is white, 0xFF0000 is red, etc.
  497.    │ ├─ in order to set the background color you would do gpu.setBackground(color)
  498.    │ ├┬ in order to set the text color you would do gpu.setForeground(color)      
  499.    │ │└─ NOTE: I recommend setting an alias for these two they get repetitive
  500.    │ └─ colors are restricted by their screens so always use a tier 3
  501.    ├─ if you want to fill in a character completely I recommend setting the background color and filling it with " ", it saves power
  502.    └┬ gpu.fill(x,y,width,height,char)
  503.     └┬ gpu fill works from the top left to the bottom right so you have to do some simple math for centering it
  504.  
  505.  ┌─EVENTS
  506.  ├─ event docs can be found here https://ocdoc.cil.li/api:event
  507.  ├─ event is a api rather than a component
  508.  ├─ in order to get the event object you can do require("event")
  509.  ├───NOTE: event is something im not to sure in so you might have to refer to the docs──
  510.  ├─┬HOW TO USE EVENTS
  511.  │ └─┬ in openui the only event call is in openui.uiEventHandlers()
  512.  │   ├─ local eventType, _, x, y = event.pull("touch")
  513.  │     ├─ quote from the docs: In driver mode your program needs to register callbacks for events (using event.listen())
  514.  │   │  then it should exit to return execution to the primary program (usually the shell).
  515.  │     │  In primary mode your program does not need to register events,
  516.  │     │  it can handle them directly using event.pull().
  517.  │   │
  518.  │   └─ the code where its at fires whenever the "touch" event is fired (i think)
  519.  └─┬USEFUL EVENTS
  520.    └┬ the only used event in openui is the "Touch event"
  521.     ├─ essentually a onClick event
  522.     └─ params for it are _, x, y
  523.  
  524. ]] return API
  525.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement