Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Custom context menu creator for ComputerCraft.
- -- Run the following commands before using this file:
- -- pastebin get Rac6Jxjg "/API/LibAppend.lua"
- -- pastebin get KA2dK07y "/API/Events.lua"
- -- pastebin get t2TvSiSU "/API/Class.lua"
- -- pastebin get 4hpwn3mn "/API/ContextMenu.lua"
- -- Documentation and the changelog for this class can be found below.
- require("/API/Events")
- ContextMenu = Class(function()
- local events = Events() -- Primarily for processing user input.
- local pressed = "" -- The name of the menu button currently being pressed.
- local buttons = {} -- Positions of buttons.
- local disabled = {} -- Functions that check if buttons are disabled.
- local dir = {left = {}, right = {}, up = {}, down = {}} -- Names of adjacent buttons.
- local pos = vector.new() -- Position of the menu.
- local size = vector.new() -- Size of the menu.
- local color = {} -- All color values.
- local alt = "" -- The button that is selected when 'alt' is pressed.
- local win = {} -- The window for the menu.
- local ro = {} -- All of the readonly methods/properties for the menu object.
- -- Processes the graphics for a button being unpressed.
- local unpress = function()
- local button = pressed
- if button ~= "" then
- pressed = ""
- ro.drawButton(button)
- end
- end
- -- Processes the graphics for a button being pressed.
- local press = function(button)
- unpress(pressed)
- if button ~= "" then
- pressed = button
- ro.drawButton(button)
- end
- end
- -- Checks if a click is within the hitbox of a button.
- local checkButtonPress = function(menuPos)
- for a, b in pairs(buttons) do
- local buttonPos = vector.new(menuPos.x-b.x+1, menuPos.y-b.y+1)
- if isBetween(buttonPos.x, 1, a:len(), true) and buttonPos.y == 1 and not disabled[a]() then
- press(a)
- return
- end
- end
- unpress()
- end
- -- Returns the position of the menu.
- ro.getPos = function()
- return pos.x, pos.y
- end
- -- Returns the size of the menu.
- ro.getSize = function()
- return size.x, size.y
- end
- -- Redraws the specified button.
- ro.drawButton = function(button)
- if disabled[button]() then
- win.setBackgroundColor(color.disabled[1])
- win.setTextColor(color.disabled[2])
- elseif pressed == button then
- win.setBackgroundColor(color.pressed[1])
- win.setTextColor(color.pressed[2])
- else
- win.setBackgroundColor(color.default[1])
- win.setTextColor(color.default[2])
- end
- if button:sub(1, 10) == "HSeparator" then
- win.setCursorPos(1, buttons[button].y)
- win.write(string.rep("\140", size.x))
- elseif button:sub(1, 10) == "VSeparator" then
- for y = 1, size.y do
- win.setCursorPos(buttons[button].x, y)
- win.write("\149")
- end
- else
- win.setCursorPos(buttons[button].x, buttons[button].y)
- win.write(button)
- end
- end
- -- Redraws the whole menu.
- ro.drawMenu = function()
- win.setBackgroundColor(color.default[1])
- win.setTextColor(color.default[2])
- win.clear()
- for a, b in pairs(buttons) do
- ro.drawButton(a)
- end
- end
- -- Processes user input for this menu.
- ro.ui = function()
- ro.drawMenu()
- while true do
- local event = events.getEvent()
- if OR(event[1], "mouse_click", "mouse_drag", "mouse_up") and event[2] == 1 then
- local menuPos = vector.new(event[3]-pos.x+1, event[4]-pos.y+1)
- if isBetween(menuPos.x, 1, size.x, true) and isBetween(menuPos.y, 1, size.y, true) then
- checkButtonPress(menuPos)
- if event[1] == "mouse_up" and not(pressed == "" or ro[pressed] == nil or disabled[pressed]()) then
- ro[pressed]()
- end
- else
- if event[1] == "mouse_click" then
- events.queueEvent(table.unpack(event))
- unpress()
- return
- end
- end
- if event[1] == "mouse_up" then
- unpress()
- return
- end
- elseif event[1] == "key" then
- if OR(event[2], keys.leftAlt, keys.rightAlt) and alt then
- if pressed == "" then
- press(alt)
- else
- unpress()
- os.pullEvent("key_up")
- return
- end
- elseif OR(event[2], keys.right, keys.left, keys.up, keys.down) then
- if dir[keys.getName(event[2])][pressed] ~= nil then
- press(dir[keys.getName(event[2])][pressed])
- end
- if pressed == "" then press(alt) end
- elseif OR(event[2], keys.enter, keys.numPadEnter) then
- if not(ro[pressed] == nil or disabled[pressed]()) then
- ro[pressed]()
- end
- unpress()
- break
- end
- end
- end
- end
- return {
- ctor = function(data)
- pos, size = data.pos, data.size
- win = window.create(term.current(), pos.x, pos.y, size.x, size.y)
- alt = data.alt
- color = data.color or {}
- color.default = color.default or {colors.lightGray, colors.black}
- color.pressed = color.pressed or {colors.gray, colors.black}
- color.disabled = colors.disabled or {colors.lightGray, colors.gray}
- for a, b in pairs(data.buttons) do
- if ro[a] == nil then ro[a] = b.main
- else error("Cannot use the menu button '" .. a .. "' because it would overwrite an already existing property in this menu.", 2)
- end
- buttons[a] = b.pos
- dir.left[a] = b.left
- dir.right[a] = b.right
- dir.up[a] = b.up
- dir.down[a] = b.down
- if a:sub(2, 10) == "Separator" then disabled[a] = function() return true end
- elseif b.disable == nil then disabled[a] = function() return false end
- else disabled[a] = b.disable end
- end
- end,
- protected = true,
- readOnly = ro
- }
- end)
- --[[ Documentation
- local exampleMenu = ContextMenu{
- • This is how you create a context menu. You can also put the curly brackets in
- parentheses but this way is less cluttery.
- pos = vector.new(<x position>, <y position>),
- • This is the coordinates on the screen the menu will be drawn.
- • This is required.
- size = vector.new(<width>, <height>),
- • This is the fixed width and height of the menu.
- • This is required.
- alt = <button name>,
- • This is the name of the menu button that will be selected first when the
- menu is opened.
- • This is required.
- color = {
- • These are the colors the menu and buttons will be depending on if a button
- is pressed or disabled or neither.
- • First value is the background color.
- • Second value is the text color.
- • All colors have a default value so none are required.
- default = {<color>, <color>},
- • These are the colors of the whole menu. They are also the colors of
- buttons that are not disabled or pressed.
- • Default background color is 'colors.lightGray'.
- • Default text color is 'colors.black'.
- pressed = {<color>, <color>},
- • These are the colors of buttons that are pressed and not disabled.
- • Default background color is 'colors.gray'.
- • Default text color is 'colors.black'.
- disabled = {<color>, <color>}
- • These are the colors of buttons that are disabled. These buttons can't
- be pressed.
- • Default background color is 'colors.lightGray'.
- • Default text color is 'colors.gray'.
- },
- buttons = {
- • These are the definitions of each button in the menu.
- • This table is required.
- • Technically no buttons are required, but not having at least 1 would make
- the whole menu redundant.
- [<button 1 name>] = {
- • The button name is the key/index of the table element.
- • There are some names that your buttons can't have. See below for more
- info.
- pos = vector.new(<x>, <y>),
- • This is the coordinates relative to the menu where the button will
- be drawn.
- • This is required.
- main = function() ... end,
- • This is the function that runs when the button is pressed.
- • This is not required for separators.
- • This is technically not required for anything else, but not having
- this would make the button redundant.
- disable = function() ... end,
- • This function determines if the button should be disabled.
- • This function must return a boolean saying if the button is
- disabled or not.
- • This is NOT required. By default this will return false.
- up = <button 2 name>,
- • This is the name of the button that will be selected when the up
- key is pressed.
- • This is NOT required.
- down = <button 3 name>,
- • This is the name of the button that will be selected when the down
- key is pressed.
- • This is NOT required.
- left = <button 4 name>,
- • This is the name of the button that will be selected when the left
- key is pressed.
- • This is NOT required.
- right = <button 5 name>,
- • This is the name of the button that will be selected when the
- right key is pressed.
- • This is NOT required.
- },
- [<button 2 name>] = {...},
- • Additional buttons follow the same format as the first one.
- ["HSeparator"] = {...},
- • This will add a horizontal line that stretch's from the left to the
- right of the whole menu.
- • This button will always be disabled and will ignore the 'main'
- function.
- • Make sure your separator's name always starts with "HSeparator". The
- name can have any text after that.
- • If you have more than one separator, each one has to have a different
- name, like "HSeparator1", "HSeparator2", etc.
- [<button 3 name>] = {...},
- [<button 4 name>] = {...},
- ["VSeparator"] = {...},
- • This follows the same rules as "HSeparator", but it draws a vertical
- line from the top to the bottom of the whole menu instead of a
- horizontal one.
- [<button 5 name>] = {...}
- }
- }
- The following methods are used in the object and therefore are protected. Avoid
- naming your buttons the same as any of these methods. Methods are case sensitive
- so your button may be the same name as long as capitalization is different.
- exampleMenu.getPos()
- • This returns the x and y coordinates of the menu.
- exampleMenu.getSize()
- • This returns the width and height of the menu.
- exampleMenu.drawMenu()
- • This redraws the whole menu.
- exampleMenu.drawButton()
- • This redraws a specific menu button.
- exampleMenu.ui()
- • This enables the menu UI.
- ]]
- --[[ Changelog
- 2024/01/02:
- • Changed all instances of 'vector2' to 'vector' to comply with the changes made
- to 'LibAppend.lua'.
- • Removed some commented debugging code.
- • Fixed some typos in the documentation.
- • Rewrote some parts of the documentation to be more descriptive.
- • Changed the color parameter to use a basic array instead of a vector (should
- reduce memory usage).
- • Rewrote the 'drawButton' method to flow better. Fixed the vertical separator
- not displaying correctly. Fixed the separators not starting at the top/left
- edges of the menu.
- • Removed the 'disable' table because it was unused.
- • Replaced/added various comments with more descriptive text.
- 2023/03/09:
- • Completely rewrote this program to comply with the changes made to 'Class.lua'
- and 'Events.lua'.
- • Removed the automatic download of required API files. The files must be
- downloaded manually.
- • Paste exposure has been returned to public.
- 2023/02/23:
- • Changed require directories from "/API/Raiu/" to "/API/".
- • Renamed vector to vector2 to reflect the changes to "/API/LibAppend.lua".
- • Reformatted the changelog so it's a bit cleaner than before.
- • Changed to unlisted until I can fix the changes made to "/API/events.lua".
- 2022/07/20:
- • Added an example for how to use the menuClass.
- 2022/07/15:
- • Added parameters for custom menu colors.
- 2022/07/13:
- • Added require functions for required APIs so the main program doesn't have to.
- 2022/06/29:
- • Updated eventListener() to events.listen().
- 2022/06/28:
- • Completely rewrote the ui and graphical code (twice). Old code was a complete
- mess and unintuitive.
- • With the new code, everything is handled within the context menu class.
- • Now works with both mouse clicks and key presses.
- ]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement