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

Creating backspace

Started by axel.codeFail(), 22 March 2014 - 01:12 AM
axel.codeFail() #1
Posted 22 March 2014 - 02:12 AM
I'm tying to make a program that allows you to type, without using the "read()" application. It all works except for when I try to backspace anything. Pressing backspace does nothing, and I'm not sure why. Any and all help would be greatly appreciated. :)/>


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

--Draw func
function draw(dx1,dy1,dx2,dy2,dcol)
  for a=dx1,dx2 do
	for b=dy1,dy2 do
	  term.setCursorPos(a,B)/>
	  term.setBackgroundColor(dcol)
	  term.write(" ")
	end
  end
end
--Text func
function text(tx,ty,tcol,tcolb,txt)
  term.setCursorPos(tx,ty)
  term.setTextColor(tcol)
  term.setBackgroundColor(tcolb)
  term.write(txt)
end
--Vars
cpX=1
cpY=1
--Loop
while true do
sleep(0)
local event, key, xpos, ypos = os.pullEvent("key")
  if key == 57 then
	if cpX < 51 then
		cpX = cpX + 1
		elseif cpX == 51 then
	  if cpY < 19 then
		cpY = cpY + 1
		cpX = 1
	  elseif cpY == 19 then
	  end
	end
  elseif key == 14 then
	if cpX > 1 then
	  draw(cpX,cpY,cpX,cpY,colors.black)
	  cpX = cpX - 1
	elseif cpX == 1 then
	   if cpY > 1 then
		  draw(cpX,cpY,cpX,cpY,colors.black)
		  cpY = cpY - 1
		  cpX = 51
	   elseif cpY == 1 then

	  end
	end
  else
	term.setCursorPos(cpX,cpY)
	term.write(tostring(k))
	cpX = cpX + 1
  end
end

Edit: updated code.
Edited on 22 March 2014 - 01:58 AM
Lyqyd #2
Posted 22 March 2014 - 02:16 AM
You're only pulling char events. Backspace (and other keys, like enter, home, end, etc.) don't throw char events.
axel.codeFail() #3
Posted 22 March 2014 - 02:18 AM
You're only pulling char events. Backspace (and other keys, like enter, home, end, etc.) don't throw char events.

I thought all the keys threw 'char' events. What kind of events do they throw?
theoriginalbit #4
Posted 22 March 2014 - 02:20 AM
I thought all the keys threw 'char' events. What kind of events do they throw?
A char event is only thrown when there is a character that can be displayed for it, i.e. alphabet, numbers, symbols
A key event is thrown by any key and it returns an ID for the key.

also remove the sleep(0) from your loop, you should not be doing this as it will clear the event queue each time, this means if someone types quick enough you'll loose events.

EDIT: added links for events
Edited on 22 March 2014 - 01:22 AM
axel.codeFail() #5
Posted 22 March 2014 - 02:23 AM
I thought all the keys threw 'char' events. What kind of events do they throw?
A char event is only thrown when there is a character that can be displayed for it, i.e. alphabet, numbers, symbols
A key event is thrown by any key and it returns an ID for the key.

also remove the sleep(0) from your loop, you should not be doing this as it will clear the event queue each time, this means if someone types quick enough you'll loose events.

EDIT: added links for events


Thanks for the help with the events, but if I removed the 'sleep(0)', wouldn't I eventually get "too long without yeilding"?
theoriginalbit #6
Posted 22 March 2014 - 02:27 AM
Thanks for the help with the events, but if I removed the 'sleep(0)', wouldn't I eventually get "too long without yeilding"?
No, because os.pullEvent yields. Any function that waits for something to happen, whether its time to elapse, or a user to input information, or a rednet message, etc, will yield.

EDIT: As a side note, sleep actually uses os.pullEvent, this is the implementation

function sleep(time)
  local timer = os.startTimer(time)
  repeat
	local event, param = os.pullEvent("timer")
  until param == timer
end
Edited on 22 March 2014 - 01:30 AM
axel.codeFail() #7
Posted 22 March 2014 - 03:02 AM
I changed the event to 'key', and added the 'tostring(k)' in. So now backspace works, however it prints the key id number, not the letter.
Edited on 22 March 2014 - 02:02 AM
theoriginalbit #8
Posted 22 March 2014 - 03:03 AM
post up your current code.
axel.codeFail() #9
Posted 22 March 2014 - 03:04 AM
post up your current code.

It is.
theoriginalbit #10
Posted 22 March 2014 - 03:06 AM
It is.
oh you updated it in the OP, didn't notice.

okay so your problem now is that you're only getting the key events, not the char events, the parameter returned with the key event is the code of the key, not its visual representation. you won't be able to specify an event when using os.pullEvent, instead you'll just need to check which event fired. Example

local event, param = os.pullEvent()
if event == "key" then
  --# it was a key event, param contains a number telling you which key
elseif event == "char" then
  --# it was a char event, param contains the character that can be printed
end
Bomb Bloke #11
Posted 22 March 2014 - 03:07 AM
Edit: Ninja'd.
Edited on 22 March 2014 - 02:08 AM
axel.codeFail() #12
Posted 22 March 2014 - 03:12 AM
But in the key events wiki page, it includes all of the keys in the id chart.
theoriginalbit #13
Posted 22 March 2014 - 03:18 AM
But in the key events wiki page, it includes all of the keys in the id chart.
that's because every key has an ID, not every key has a char. the ID is what you use to check which key was pressed, the char is what you use to display the character. there is a way to get the character from the ID but don't use it, use the char event!
theoriginalbit #14
Posted 22 March 2014 - 03:45 AM
so you indicate this is a replacement for the read function, is it just to display information they type, or do you intend on using the information that they've input too?
axel.codeFail() #15
Posted 22 March 2014 - 03:52 AM
In the end, I intend to use it as a variable. But first I just wanted to see if the typing was even possible.
theoriginalbit #16
Posted 22 March 2014 - 04:04 AM
okay fair enough :)/> well when using the variable you can simplify the code massively

Code Example

local function simplisticRead()
  local input = ""
  local startX, startY = term.getCursorPos()
  term.setCursorBlink(true)

  local function draw()
    term.setCursorPos(startX, startY)
    term.clearLine()
    write(input)
  end

  while true do
    local event = {os.pullEventRaw()}
    if event[1] == "key" then
      if event[2] == keys.backspace then
        input = input:sub(1, #input - 1)
      elseif event[2] == keys.enter then
        term.setCursorBlink(false)
        return input
      end
    elseif event[1] == "char" then
      input = input..event[2]
    end
    draw()
  end
end

Code Example (Commented)

local function simplisticRead()
  --# here we store the input
  local input = ""

  --# we then get the position the cursor currently is at
  local startX, startY = term.getCursorPos()

  --# we then activate the curor blink
  term.setCursorBlink(true)

  local function draw()
    --# set the cursor position to where it started
    term.setCursorPos(startX, startY)
    --# clear the line
    term.clearLine()
    --# write the input to the screen
    write(input)
  end

  --# now lets gather the input
  while true do
    --# wait here until something happens, in this case we use os.pullEventRaw so someone cannot hold CTRL+T and stop the function
    local event = {os.pullEventRaw()}
    --# if the event was a key event
    if event[1] == "key" then
      --# and the event was for the backspace key
      if event[2] == keys.backspace then
        --# remove the last characer; #input is the length of the input
        input = input:sub(1, #input - 1)

      --# if the event was for the enter key
      elseif event[2] == keys.enter then
        --# turn off the cursor blink
        term.setCursorBlink(false)
        --# return the input back to the calling function
        return input
      end

    --# if the event was a character event
    elseif event[1] == "char" then
      --# add the character to the input, this includes characters such as space
      --# but not tab, you'll need to check for keys.tab in the key event for that
      input = input..event[2]
    end

    --# draw the input
    draw()
  end
end
axel.codeFail() #17
Posted 22 March 2014 - 04:13 AM
okay fair enough :)/> well when using the variable you can simplify the code massively

Man, I was hoping this was an original thought. :P/>

term.setCursorBlink(true)

local event = {os.pullEventRaw()}

Those just blew my mind. I never knew you could make the cursor blink, without making something on your own, and I also never thought to use a table for 'os.pullEvent'.
:o/>

EDIT :
The information is great, but would I be able to use two inputs with this? I was planning on making a login system, with a username and password, using this.
Edited on 22 March 2014 - 03:21 AM
theoriginalbit #18
Posted 22 March 2014 - 04:29 AM
Man, I was hoping this was an original thought. :P/>
there's a few reimplementations of the read function out there, each with their own benefits and objectives; for example my own implementation works on refining the vanilla one to fix bugs, make it more efficient, add some more features, but all while working with existing scripts. On that note I'll have to update that when 1.6 comes out, a fair new features :P/>

Those just blew my mind. I never knew you could make the cursor blink, without making something on your own
yep, it may be worth having a read of all the functions that ComputerCraft adds on top of Lua. wiki link.

and I also never thought to use a table for 'os.pullEvent'.
its the best way to gather arguments when dealing with lots of different kinds of input varying from what you'd call the variables, all the way to the quantity of variables.
CometWolf #19
Posted 22 March 2014 - 09:31 AM
The information is great, but would I be able to use two inputs with this? I was planning on making a login system, with a username and password, using this.
Yes, quite easily infact. Just store them in a table, and use a variable for controlling which is currently active.

local tInputs = { --set up the fields
  [1] = {
    x = 5,
    y = 3,
    value = "blahBlah"
  }
  [2] = {
    x = 5,
    y = 4,
    value = "herp derp"
  }
}
local active = 1 --which field is active
while true do
  local tEvent = {os.pullEvent()}
  local input = tInputs[active]
  if tEvent[1] == "char" then
    local char = tEvent[2]
    input.value = input.value..char
    term.setCursorPos(input.x,input.y)
    term.write(input.value)
  elseif tEvent[1] == "key" then
    local key = tEvent[2]
    if key == keyes.up then
	  active = math.max(1,active-1) --change active field up one
    elseif key == keyes.down then
	  active = math.min(#tInputs,active+1) --change active field down one
    end
  end
end
You get the idea. If you're running on an advanced computer you might want to intergrate some cursor support aswell. I can post up an excerpt of some of my code if you wanna see how i handle all this.