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

How to make that

Started by Writer, 24 August 2014 - 11:05 PM
Writer #1
Posted 25 August 2014 - 01:05 AM
I'm starting my own CC OS, called Storm OS. I made only one part, which is the password window (yes, an independent program). The password/username check doesn't work, so don't mind with that. This window will be kinda similar to the windows 8 password window. Now, I want that, when I have my input for username/password, I can just click in the white pixels under each word, select it and read what's inside. However, I want a way to, when it is reading, if I click in the other white pixel, it moves the reading operation to the other white bar, mantaining the words previously written in the other white bar. So, I have 2 white bars and I want a way to be able to move to one other bar while wirting in the other one, mantaining the words written. Then I will make so you either can only input x number of caracters, or move horizontly the bar. However, up to now, I can input, but I can't change bars while inputing. Here's the code:
local color = {colors.cyan, colors.lightBlue, colors.orange, colors.green, colors.red, colors.magenta, colors.purple, colors.blue}
local pos = math.random(1, #color)
local background = color[pos]
local username = ""
local password = ""

function image()
  local y = 7
  for i = 1, 5 do
	term.setCursorPos(13, y)
	term.setBackgroundColor(colors.gray)
	term.write("		")
	y = y + 1
  end
end

function names(text, x, y)
  term.setBackgroundColor(background)
  term.setCursorPos(x, y)
  print(text)
  for i = 1, 10 do
	paintutils.drawPixel(x, y + 1, colors.white)
	x = x + 1
  end
  term.setBackgroundColor(background)
end

function click()
  local event, button, x, y = os.pullEvent("mouse_click")
  for i = 22, 32 do
	if x == i then
	  if y == 8 then
		if button == 1 then
		  term.setCursorPos(22, 8)
		  username = read()
		end
	  elseif y == 11 then
		if button == 1 then
		  term.setCursorPos(22, 11)
		  username = read()
		end
	  end
	end
  end
end

term.setBackgroundColor(background)
term.clear()
image()
names("Username", 22, 7)
names("Password", 22, 10)
term.setTextColor(colors.black)
term.setCursorPos(22, 8)
username = read()
term.setCursorPos(22, 11)
password = read()
while true do
click()
end

And yes, I'm making a BIG thing, so help?…

BTW, I'm in 1.7.10, if that changes a bit the game.
Edited on 24 August 2014 - 11:06 PM
Bomb Bloke #2
Posted 25 August 2014 - 03:53 AM
The basic "read()" function won't be suitable for this. It expects the user to fully enter a string and then hit return, and that's that.

So first you'll want your own version, one that you can alter easily. This one's a bit primitive, but for the sake of example:

local textField = ""

local function textFieldRead()
	while true do
		local myEvent = {os.pullEvent()}
		
		if myEvent[1] == "char" then
			textField = textField .. myEvent[2]
			term.write(myEvent[2])
			
		elseif myEvent[1] == "key" then
			if myEvent[2] == keys.backspace and #textField > 0 then
				textField = textField:sub(1,#textField-1)
				
				local xPos,yPos = term.getCursorPos()
				term.setCursorPos(xPos-1,yPos)
				term.write(" ")
				term.setCursorPos(xPos-1,yPos)
				
			elseif myEvent[2] == keys.enter then
				return
				
			end
		end
	end
end

textFieldRead()

print("")
print("Field contained "..textField)

Next you want to rig things so that the function can handle multiple text fields at once. This involves having a way to tell said fields apart, and switch between them:

local textFields = {}

local function newTextField(name, xPos, yPos, length)
	textFields[name] = {["text"] = "", ["xPos"] = xPos, ["yPos"] = yPos, ["curX"] = xPos, ["curY"] = yPos, ["length"] = length}
	term.setBackGroundColour(colours.white)
	term.setCursorPos(xPos, yPos)
	term.write(string.rep(" ",length))
	term.setBackgroundColour(colours.black)
end

local function textFieldRead(currentField)
	term.setCursorPos(textFields[currentField].xPos, textFields[currentField].yPos)

	while true do
		local myEvent = {os.pullEvent()}
		
		if myEvent[1] == "char" and #textFields[currentField].text < textFields[currentField].length then
			textFields[currentField].text = textFields[currentField].text .. myEvent[2]
			term.write(myEvent[2])
			
		elseif myEvent[1] == "key" then
			if myEvent[2] == keys.backspace and #textFields[currentField].text > 0 then
				textFields[currentField].text = textFields[currentField].text:sub(1,#textFields[currentField].text-1)
				
				local xPos,yPos = term.getCursorPos()
				term.setCursorPos(xPos-1,yPos)
				term.write(" ")
				term.setCursorPos(xPos-1,yPos)
				
			elseif myEvent[2] == keys.enter then
				return
				
			end
		
		elseif myEvent[1] == "mouse_click" then
			for field,_ in pairs(textFields) do
				if myEvent[3] >= textFields[field].xPos and myEvent[3] < textFields[field].xPos + textFields[field].length and myEvent[4] == textFields[field].yPos then
					textFields[currentField].curX,textFields[currentField].curY = term.getCursorPos()
					currentField = field
					term.setCursorPos(textFields[currentField].curX,textFields[currentField].curY)
					break
				end
			end
		end
	end
end

term.setBackgroundColour(colours.black)
term.clear()

newTextField("Username", 1, 1, 7)
newTextField("Password", 1, 2, 7)

textFieldRead()

term.clear()
term.setCursorPos(1,1)

for key,value in pairs(textFields) do
	print("\""..key.."\" contained \""..value.."\".")
end

Note that I haven't tested any of this, so there may be some small mistakes in there that need correcting before it'll actually run. It's intended to give you some idea how to approach the problem, so even if it's functional, you'll want to give it a fair bit of polish.
Writer #3
Posted 25 August 2014 - 01:04 PM
I saw your code and was like :huh:/> . Then I saw it line by line and I understood. But I have some questions:
in the first code:
what value is myEvent[2] ? Is it the char pressed?
what does textField:sub(1, #textField-1) ? Is it diferent than string.sub?
the locals xPos and yPos, after the string has been decreased, does change?

in the second code:
why isn't name used in the first function?
what does 'length' do in the first function?
what does string.rep?
why is '#textFields[currentField].text<textFields[currentField].length' and 'textFields[currentField].text = textFields[currentField].text .. myEvent[2]' necessary? What does it?
this one I never understood: what does 'for field,_ in pairs(textFields) do'? Like, what does 'field,_'? For what is it?
what is the value of myEvent[3] ?
in the line where there if the last elseif, currentFields = field shouldn't be right under the for loop? Or does CC recognize it automaticaly?

Sorry if these questions are a bit silly, but I don't know everything about CC. [and I usually make mistakes… :P/> ]
Bomb Bloke #4
Posted 25 August 2014 - 03:36 PM
what value is myEvent[2] ? Is it the char pressed?

Yes, it represents the char pressed - assuming the captured event was a character event. If it's a key event, then it's the key code. If it's a mouse click event, then it's the button pressed.

what does textField:sub(1, #textField-1) ? Is it diferent than string.sub?

Pretty much the same thing. textField:sub(1, #textField-1) functions the same as string.sub(textField, 1, #textField-1), but is ever so slightly shorter.

the locals xPos and yPos, after the string has been decreased, does change?

They're only used to determine the position of the last typed character, so that a space can be written over it.

why isn't name used in the first function?

It is used in the first function.

what does 'length' do in the first function?

It's used to determine the length of the text field - how many characters you can type into it at maximum.

what does string.rep?

It returns a string which is equal to its first argument repeated an amount equal to its second argument.

For example, string.rep("asdf",3) returns "asdfasdfasdf". See this page for more details on string-related functions.

'#textFields[currentField].text<textFields[currentField].length'

"current length of the text in the text field < maximum allowed length of the text in the text field"

'textFields[currentField].text = textFields[currentField].text .. myEvent[2]'

"let the text in the field equal the current text in the field plus whatever character the current char event represents".

this one I never understood: what does 'for field,_ in pairs(textFields) do'? Like, what does 'field,_'? For what is it?

It starts a loop which repeats one time for every entry in the "textFields" table. The two variables, "field" and "_", each get assigned a table key and value respectively with each iteration.

"_" is generally used as a variable name when you don't care about the value in concern. I just wanted the key names. In this case, the first run of the loop might set the "field" variable to "Username", and the second might set it to "Password".

Pairs loops are generally used with tables specifically. You can read more about them in this tables tutorial.

what is the value of myEvent[3] ?

Usually nothing, but in the case of a mouse click event, it's the x-position where the click occurred. Compare my usage of "myEvent" to this list.

in the line where there if the last elseif, currentFields = field shouldn't be right under the for loop? Or does CC recognize it automaticaly?

The loop in concern checks each text field in the textFields table to see if a given mouse click event signifies a click on any of them. If it finds a match, then it sets clicked field as the "current" one which the user wants to type into, then breaks out of the search loop.
Writer #5
Posted 25 August 2014 - 05:51 PM
Thank you so much! I will use this code for sure, maybe with some litle changes, so I don't copy you. Thanks for the explanation ;)/>.
Writer #6
Posted 25 August 2014 - 06:55 PM
Another quick question: if the textFieldRead function has a param, how does it start if is called without any param defined?
Bomb Bloke #7
Posted 25 August 2014 - 07:20 PM
That's one of a few mistakes in my code. The long and short of it is that it doesn't: You'd want to call it with the name of a text field in order for it to work.

Eg textFieldRead("Username").
Writer #8
Posted 25 August 2014 - 07:25 PM
Oh, ok. I thought it was being called somewhere and I wasn't seeing. :P/>
Writer #9
Posted 25 August 2014 - 07:47 PM
I think I found one error and one redundance: I started writing the code and the function newTextField kinda doesn't work because of the quotes, putting them in the wrong place. It is like this: string[name] = {["text"] = "", ["xPos"] = xPos, ["yPos"] = yPos, ["curX"] = xPos, ["curY"] = yPos, ["length"] = length} (changed textFields to string). And I'm pretty sure you can put this like string[name] = {"text" = "", "xPos" = xPos, "yPos" = yPos, "curX" = xPos, "curY" = yPos, "length" = length}, don't you?

–edit
Found a solution to the problem of the misplaced quotes = substituit ["text"] = "" for ["text"] = ''. The "redundance" is necessary. I figured out…
Edited on 25 August 2014 - 07:15 PM
Writer #10
Posted 26 August 2014 - 12:41 AM
This seems odd: I alread wrote the code, but it is claming a error on the 'term.write(string.rep(" ", length))', claming "attempt to call nil". What is wrong?
KingofGamesYami #11
Posted 26 August 2014 - 02:35 AM
Attempt to call nil means one of the functions you are calling is nil. Usually means you misspelled the functinon
EG:

term.write( "hello" )
term.wirte( "world" )
results in:

hello string:2:attempt to call nil
Writer #12
Posted 26 August 2014 - 03:05 AM
Yes, I knew that. What seems wrong is that string.rep() is in the lua string api, and I'm using it correctly, but still, it doesn't work.
Bomb Bloke #13
Posted 26 August 2014 - 03:18 AM
Assuming you've entered "term.write" correctly, and "string.rep" correctly, I'd suspect you're looking at the wrong line.

Even if that's the line the Lua interpretor is pointing at, take a look at how you've entered the line immediately above. Sometimes problems in one line can lead to errors triggering on the next.
Writer #14
Posted 26 August 2014 - 11:14 PM
Now it worked. For some reason, when I loged out-loged in, it started working properly.
I think I found another issue: It seems like that the table for the event doesn't work properly. I think it's because the "key" event overwrites the "char" event, in a way that, if you press any key on your keyboard, it will register the event as a key event, showing the number of the key pressed. I found the solution to be just creating an local variable for the "char" event and creating a table for the general key pressing thing.
Edited on 26 August 2014 - 09:17 PM