This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
asparagus's profile picture

Scrolling Menu Help

Started by asparagus, 12 April 2016 - 04:58 AM
asparagus #1
Posted 12 April 2016 - 06:58 AM
This is something that's been causing me quite a headache for the past four or so hours I've been working on it.
Currently I am setting up a sorting system and it's going swimmingly. However, I've had a slight blockade in the way that I wanted to setup the terminal menu to view stored items.
I'm currently using this nitty-gritty bit of code to pull the directory of "Items" and display every item in the folder as a scrollable list.

list = fs.list("Items/")
for i,v in ipairs(list) do
--i being the iterator, v being the value
  print(v) --print value
end
--OR
list = fs.list("Items/")
for i = 1,#list do
   print(list[i])
end
local scrollBuffer = {}
local _,maxY = term.getSize()
for k, v in pairs(list) do
  print(k .. " : " .. v)
  table.insert(scrollBuffer,k.." : ".. v)
end
local y = #scrollBuffer
while true do
  local _, direction = os.pullEvent("mouse_scroll")
  if direction == 1 then --# direction 1 is when you scroll the wheel one way
		if  y < #scrollBuffer-maxY then
		  y=y+1
		end
		term.clear()
		term.setCursorPos(1,1)
		for i = 1, maxY-1 do
		  if not scrollBuffer[i+y] then break end
		  print(scrollBuffer[i+y])
		end
		term.write(scrollBuffer[maxY+y])
  elseif direction == -1 then --# -1 is the only direction
		if y~=0 then --# not going to explain anymore, as it all repeats from here
		  y=y-1
		end
		term.clear()
		term.setCursorPos(1,1)
		for i = 1, maxY-1 do
		  if not scrollBuffer[i+y] then break end
		  print(scrollBuffer[i+y])
		end
		term.write(scrollBuffer[maxY+y])
  end
end
This works well, the problem I am experiencing is adding the ability to click on individual items. From past experiences with "os.pullEventRaw("mouse_click")" I used coordinates to define clickability but I don't know how to approach this list doodad.
Any help is appreciated.
Bomb Bloke #2
Posted 12 April 2016 - 07:26 AM
Say we simplify what you've got a little bit:

local function filePicker(path)
	local y = 1
	local list = fs.list(path)
	local maxX, maxY = term.getSize()

	term.clear()
	for i = y, y + maxY - 1 do
		if not list[i] then break end
		term.setCursorPos(1, i - y + 1)
		term.write(i .. " : " .. list[i])
	end

	while true do
		local myEvent = {os.pullEvent()}

		if myEvent[1] == "mouse_scroll" then
			if myEvent[2] == 1 and  y < #list - maxY + 1 then
				y = y + 1
			elseif myEvent[2] == -1 and y ~= 0 then
				y = y - 1
			end
			
			term.clear()
			for i = y, y + maxY - 1 do
				if not list[i] then break end
				term.setCursorPos(1, i - y + 1)
				term.write(i .. " : " .. list[i])
			end
		end
	end
end

local result = filePicker("Items/")

… if we then added a bit in there along these lines:

		elseif myEvent[1] == "mouse_click" then
			return list[???]
		end

… then you can probably figure out what ??? should be, given that myEvent[4] is the line number the user clicked on, and y - 1 is the number of lines they've scrolled down. :)/>
asparagus #3
Posted 12 April 2016 - 07:16 PM
Say we simplify what you've got a little bit…

So, then wouldn't you just need to do something like

elseif myEvent[1] == "mouse_click" then
                        return list[myEvent[4], y]
In the myEvent's table? And then follow return list[myEvent[4], y] with whatever you'd like the program to due, such as run another program based upon the input from the list?
Bomb Bloke #4
Posted 13 April 2016 - 12:39 AM
Well, no, you want to get the entry in the list that the user clicked on. If y - 1 is the number of lines the user scrolled down, and myEvent[4] is the number of the display row that was clicked on, then then myEvent[4] + y - 1 is the index within the list table with the relevant data:

return list[myEvent[4] + y - 1]
Edited on 12 April 2016 - 10:39 PM
asparagus #5
Posted 13 April 2016 - 06:34 AM
Well, no…
Before you had replied, I tested that exact method, with no success. I'm not sure if it makes anything clickable because I can't put any test code after:

elseif myEvent[1] == "mouse_click" then
					    return list[myEvent[4] + y - 1]
without it generating an infinite end loop.
Bomb Bloke #6
Posted 13 April 2016 - 08:54 AM
Ah, sounds like you may not be familiar with building functions yet - an "end" is always expected immediately after a "return" statement, because once that return is processed the function ceases and the script carries on from the point where it was first called. Any actionable code you stick immediately after a "return" will never execute, so the compiler calls you out on it if you attempt to put some there.

That is to say, add your code in under this line down the bottom, after the point where the function was called:

local result = filePicker("Items/")  --# "result" is assigned whatever value the function returned, in this case the name of the file the user clicked on.

http://lua-users.org/wiki/FunctionsTutorial
asparagus #7
Posted 13 April 2016 - 10:43 PM
Ah, sounds like you…

Never had to use the return function before in any of my programs. I read up on it from the link you left and seemed to grasp a basic understanding of it.
So, in basics if I get this right. During the function if you return a value then it is callable after the function ends? So, for my setup I'd need to call something like
 shell.run(myEvent[4] + y - 1) 
after the function to be able to use that value? Because previously stated, "myEvent[4] + y - 1 is the index within the list table with the relevant data". Am I understanding this right?
Edited on 13 April 2016 - 08:44 PM
KingofGamesYami #8
Posted 13 April 2016 - 11:15 PM
Uhm, no. The function returns the value - wherever you called the function, the result is returned. read() returns what the user typed, if you've used it before.
Bomb Bloke #9
Posted 14 April 2016 - 12:42 AM
That is to say, "return" is not a function, it's a Lua keyword that you place within functions.

Look again at this line:

local result = filePicker("Items/")

This calls the "filePicker()" function, passing it "Items/" as a parameter.

The function assigns that value to an internal "path" variable, and uses it to get a file list (by passing it to fs.list() as a parameter, which in turn returns a table containing the list of files found at that location), which it assigns to the "list" variable.

It then handles user input, and returns list[myEvent[4] + y - 1] when the user clicks somewhere. At this point, the script's flow of execution returns to the line where filePicker() was first called, and the string that was in the specified index of the table is finally assigned to the "result" variable.

So after that, you'd just do:

shell.run(result)

Heck, you could skip the assignment and just pass the filePicker() return value directly to shell.run(), if you didn't need to save it anywhere:

shell.run(filePicker("Items/"))
asparagus #10
Posted 14 April 2016 - 04:14 AM
That is to say, "return" is not a function, it's a Lua keyword that you place within functions.

Look again at this line:

local result = filePicker("Items/")

This calls the "filePicker()" function, passing it "Items/" as a parameter.

The function assigns that value to an internal "path" variable, and uses it to get a file list (by passing it to fs.list() as a parameter, which in turn returns a table containing the list of files found at that location), which it assigns to the "list" variable.

It then handles user input, and returns list[myEvent[4] + y - 1] when the user clicks somewhere. At this point, the script's flow of execution returns to the line where filePicker() was first called, and the string that was in the specified index of the table is finally assigned to the "result" variable.

So after that, you'd just do:

shell.run(result)

Heck, you could skip the assignment and just pass the filePicker() return value directly to shell.run(), if you didn't need to save it anywhere:

shell.run(filePicker("Items/"))

Still doesn't make anything Clickable….

Full code:

local function filePicker(path)
	    local y = 1
	    local list = fs.list(path)
	    local maxX, maxY = term.getSize()
	    term.clear()
	    for i = y, y + maxY - 1 do
			    if not list[i] then break end
			    term.setCursorPos(1, i - y + 1)
			    term.write(i .. " : " .. list[i])
	    end
	    while true do
			    local myEvent = {os.pullEvent()}
			    if myEvent[1] == "mouse_scroll" then
					    if myEvent[2] == 1 and  y < #list - maxY + 1 then
							    y = y + 1
					    elseif myEvent[2] == -1 and y ~= 0 then
							    y = y - 1
	  elseif myEvent[1] == "mouse_click" then
					    return list[myEvent[4] + y - 1]
			   
    end
					    end
					   
					    term.clear()
					    for i = y, y + maxY - 1 do
							    if not list[i] then break end
							    term.setCursorPos(1, i - y + 1)
							    term.write(i .. " : " .. list[i])
					   
	  end
			    end
	    end

 
  local result = filePicker("Items/")
  shell.run(result)
Edited on 14 April 2016 - 02:16 AM
Bomb Bloke #11
Posted 14 April 2016 - 05:44 AM
Let's repeat what you've written there with some proper indentation:

Spoiler
local function filePicker(path)
	local y = 1
	local list = fs.list(path)
	local maxX, maxY = term.getSize()
	term.clear()

	for i = y, y + maxY - 1 do
		if not list[i] then break end
		term.setCursorPos(1, i - y + 1)
		term.write(i .. " : " .. list[i])
	end

	while true do
		local myEvent = {os.pullEvent()}
		if myEvent[1] == "mouse_scroll" then  --# Given that the code in the block below only runs if myEvent[1] is "mouse_scroll"...
			if myEvent[2] == 1 and  y < #list - maxY + 1 then
				y = y + 1
			elseif myEvent[2] == -1 and y ~= 0 then
				y = y - 1
			elseif myEvent[1] == "mouse_click" then  --# ... we hence know that myEvent[1] can never be "mouse_click" right here.
				return list[myEvent[4] + y - 1]
			end
		end

		term.clear()

		for i = y, y + maxY - 1 do
			if not list[i] then break end
			term.setCursorPos(1, i - y + 1)
			term.write(i .. " : " .. list[i])
		end
	end
end

local result = filePicker("Items/")
shell.run(result)

What you're going for is:

Spoiler
local function filePicker(path)
        local y = 1
        local list = fs.list(path)
        local maxX, maxY = term.getSize()

        term.clear()
        for i = y, y + maxY - 1 do
                if not list[i] then break end
                term.setCursorPos(1, i - y + 1)
                term.write(i .. " : " .. list[i])
        end

        while true do
                local myEvent = {os.pullEvent()}

                if myEvent[1] == "mouse_scroll" then
                        if myEvent[2] == 1 and  y < #list - maxY + 1 then
                                y = y + 1
                        elseif myEvent[2] == -1 and y ~= 0 then
                                y = y - 1
                        end

                        term.clear()
                        for i = y, y + maxY - 1 do
                                if not list[i] then break end
                                term.setCursorPos(1, i - y + 1)
                                term.write(i .. " : " .. list[i])
                        end
                elseif myEvent[1] == "mouse_click" then
                        return list[myEvent[4] + y - 1]
                end
        end
end

local result = filePicker("Items/")
shell.run(result)
asparagus #12
Posted 14 April 2016 - 05:55 AM
Let's repeat what you've written there with some proper indentation:

Spoiler
local function filePicker(path)
	local y = 1
	local list = fs.list(path)
	local maxX, maxY = term.getSize()
	term.clear()

	for i = y, y + maxY - 1 do
		if not list[i] then break end
		term.setCursorPos(1, i - y + 1)
		term.write(i .. " : " .. list[i])
	end

	while true do
		local myEvent = {os.pullEvent()}
		if myEvent[1] == "mouse_scroll" then  --# Given that the code in the block below only runs if myEvent[1] is "mouse_scroll"...
			if myEvent[2] == 1 and  y < #list - maxY + 1 then
				y = y + 1
			elseif myEvent[2] == -1 and y ~= 0 then
				y = y - 1
			elseif myEvent[1] == "mouse_click" then  --# ... we hence know that myEvent[1] can never be "mouse_click" right here.
				return list[myEvent[4] + y - 1]
			end
		end

		term.clear()

		for i = y, y + maxY - 1 do
			if not list[i] then break end
			term.setCursorPos(1, i - y + 1)
			term.write(i .. " : " .. list[i])
		end
	end
end

local result = filePicker("Items/")
shell.run(result)

What you're going for is:

Spoiler
local function filePicker(path)
		local y = 1
		local list = fs.list(path)
		local maxX, maxY = term.getSize()

		term.clear()
		for i = y, y + maxY - 1 do
				if not list[i] then break end
				term.setCursorPos(1, i - y + 1)
				term.write(i .. " : " .. list[i])
		end

		while true do
				local myEvent = {os.pullEvent()}

				if myEvent[1] == "mouse_scroll" then
						if myEvent[2] == 1 and  y < #list - maxY + 1 then
								y = y + 1
						elseif myEvent[2] == -1 and y ~= 0 then
								y = y - 1
						end

						term.clear()
						for i = y, y + maxY - 1 do
								if not list[i] then break end
								term.setCursorPos(1, i - y + 1)
								term.write(i .. " : " .. list[i])
						end
				elseif myEvent[1] == "mouse_click" then
						return list[myEvent[4] + y - 1]
				end
		end
end

local result = filePicker("Items/")
shell.run(result)

That makes more sense now, I was under the impression that as long as it was an elseif then it would identify as it's own system.