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

[LUA] Scrolling a certain amount of text without deleting it?

Started by Heracles421, 09 January 2013 - 07:51 AM
Heracles421 #1
Posted 09 January 2013 - 08:51 AM
I want to scroll text that uses more space than the terminal height, the problem is: whenever I try to scroll up, and then back down the text is gone and doesn't re-appear
Spoiler

-- Draw a line at <sheight>
function line(sheight)
  s = ""
  swidth = term.getSize()
  term.setCursorPos(1,sheight)
  for i = 1, swidth do
    s = s.."-"
  end
  print(s)
end

-- Print text centered at line <sheight>
function printC(text, sheight)
  swidth = term.getSize()
  swidth = math.ceil((swidth/2) - (string.len(text)/2))
  term.setCursorPos(swidth, sheight)
  print(text)
end

-- Clear screen and set cursor pos
function cScreen(swidth, sheight)
  term.clear()
  term.setCursorPos(swidth, sheight)
end

-- Wait for key pressing
function waitForKey()
  while true do
    local sEvent,param = os.pullEvent("key")
    if sEvent == "key" then
      if param == 200 then
        term.scroll(-1)
      elseif param == 208 then
        term.scroll(1)
      elseif param ~= 200 or 208 then
        startup()
      end
    end
  end
end

-- Print a microphone
function mic()
  print("                      .::. ")
  print("                     ///\\\\\\")
  print("                     ||||||")
  print("                 HH  ||||||  HH")
  print("                 HH==========HH")
  print("                 HH==========HH")
  print("                 HH  ######  HH")
  print("                 HH   ####   HH")
  print("                 HH    ##    HH")
  print("                 HH          HH")
  print("                   \\\\      // ")
  print("                     \\\\  //   ")
  print("                    ___\\/___   ")
end

-- Print a progress bar
function progressBar()
  swidth, sheight = 22,15
  local function doWrite(char)
    term.setCursorPos(22, 15)
    term.setCursorBlink(false)
    write("Loading "..char)
    sleep(0.1)
  end
  for i = 1,10 do
    doWrite("|")
    doWrite("/")
    doWrite("-")
    doWrite("\\")
  end
  getMessage()
end

function getMessage()
  input = answer
  answer = textutils.urlEncode(answer)
  http.request("http://api.wolframalpha.com/v2/query?appid=5KGYY5-YE2JLLG74H&input="..answer.."&format=plaintext")
  local sEvent, url, sourceText = os.pullEvent()
  if sEvent == "http_success" then
    local respondedText = sourceText.readAll()
    text = {}
    index = 1
    for content in respondedText:gmatch "<plaintext>(.-)</plaintext>" do
      table.insert(text, content)
      text[index] = string.gsub(text[index],"&amp;apos;","'")
      text[index] = string.gsub(text[index],"&amp;quot;","\"")
      index = index + 1
    end
    while true do
      cScreen(1,1)
      line(1)
      printC("CCiri V "..version, 2)
      line(3)
      if text[1] ~= nil then
        write("Input: "..input, 5)
        print("\n")
        index = 2
        for i = 1,#text - 1 do
          print(text[index].."\n")
          index = index + 1
        end
        parallel.waitForAny(waitForKey)
      else
        printC("Output: No answer found",7)
      end
      waitForKey()
    end
  elseif sEvent == "http_failure" then
    progress = false
    cScreen(1,1)
    line(1)
    printC("CCiri V "..version, 2)
    line(3)
    write("Couldn't contact server")
    waitForKey()
  end
end  

function cciri()
  cScreen(1,1)
  line(1)
  printC("CCiri V "..version, 2)
  line(3)
  mic()
  line(17)
  line(19)
  term.setCursorPos(1,17)
  write("> ")
  answer = read()
end

function startup()
  cciri()
  -- parallel.waitForAny(wait,progressBar)
  getMessage()
end

version = "1.0"
startup()
GopherAtl #2
Posted 09 January 2013 - 09:00 AM
That's just how the built-in term.scroll works, I'm afraid. You'd have to implement something yourself to save more lines than the screen and allow scrolling them.
theoriginalbit #3
Posted 09 January 2013 - 09:03 AM
take a look at one i prepared late last year for someone else http://pastebin.com/mqwMPSbB hopefully it can give you some ideas :)/>
Heracles421 #4
Posted 09 January 2013 - 11:15 AM
That's just how the built-in term.scroll works, I'm afraid. You'd have to implement something yourself to save more lines than the screen and allow scrolling them.

Well, ATM all the lines are stored in a table, but I'm not sure how to scroll those lines. Usually, the table has around 20 - 50 lines of text

take a look at one i prepared late last year for someone else http://pastebin.com/mqwMPSbB hopefully it can give you some ideas :)/>/>/>

Thanks, but that doesn't work at all because you're printing every line in the table after x number of seconds, and then starting to print from the first line again when you reach the final line
theoriginalbit #5
Posted 09 January 2013 - 02:25 PM
Thanks, but that doesn't work at all because you're printing every line in the table after x number of seconds, and then starting to print from the first line again when you reach the final line

I'm not printing every line. it prints as many lines as can fit on the screen starting from the given position. the one second means it scrolls every one second. that was their use case.
Heracles421 #6
Posted 09 January 2013 - 02:41 PM
Thanks, but that doesn't work at all because you're printing every line in the table after x number of seconds, and then starting to print from the first line again when you reach the final line

I'm not printing every line. it prints as many lines as can fit on the screen starting from the given position. the one second means it scrolls every one second. that was their use case.

I wasn't clear enough with my answer, what I ment is that you print all the lines that can be in the screen, after x number of seconds you print the next line at the bottom, scrolling all the other lines upwards, thus making the top one disappear. Once you finish printing the full table it'll print everything again from the first line.
I cannot use this method because of multiple reasons: 1. The table I am using stores paragraphs, not single lines, therefore I cannot print line by line. 2. I want to scroll once the user hits the up/down arrow keys, not automatically
theoriginalbit #7
Posted 09 January 2013 - 02:46 PM
thats a little bit of a better explanation :)/> ok so user pressing keys is easy to change. just remove the timer and replace it with the key press for keys.up and keys.down when they are pressed you just appropriately change the scroll start… as for the paragraph thing, using print or write doesn't word wrap the line, could you not just have it store each line into the table on its own instead of by paragraph?
Heracles421 #8
Posted 09 January 2013 - 02:59 PM
As for the paragraph thing, using print or write doesn't word wrap the line, could you not just have it store each line into the table on its own instead of by paragraph?

Nope, because I'm requesting some things from an external web page and the answers are sent as paragraphs, not line by line
theoriginalbit #9
Posted 09 January 2013 - 03:03 PM
well your going to have to split the paragraph into lines at some point to print anyway.
Heracles421 #10
Posted 09 January 2013 - 03:10 PM
well your going to have to split the paragraph into lines at some point to print anyway.

How could I do that? The problem is that the web page sends multiple answers, which have around 3 to 70 words each, so I store each answer in a table which then is printed.
You can try out the program if you want to, just to see how it works.

pastebin get PRUuJxf6 cciri
theoriginalbit #11
Posted 09 January 2013 - 03:17 PM
does that code actually work for you? It crashes for me, and looking at what line its on I think its because I'm getting events that aren't 'http' events and your code panics when it cant .readAll() on it
Heracles421 #12
Posted 09 January 2013 - 03:21 PM
does that code actually work for you? It crashes for me, and looking at what line its on I think its because I'm getting events that aren't 'http' events and your code panics when it cant .readAll() on it
It does indeed, actually it works on a server, it works on the Computercraft University. Maybe it's just your computer derping?
theoriginalbit #13
Posted 09 January 2013 - 03:26 PM
Interesting… I'm using CC-Emu… Anyways as for the 'content' maybe split it into chunks that are no longer than the width of the screen, then store those individual chunks in the table?
Heracles421 #14
Posted 09 January 2013 - 03:28 PM
Interesting… I'm using CC-Emu… Anyways as for the 'content' maybe split it into chunks that are no longer than the width of the screen, then store those individual chunks in the table?

Might work, the only downside is the formatting, because I am splitting one answer from another since usually I get more that 1 answer. Not sure if this will be possible at all if I split the content into individual lines
theoriginalbit #15
Posted 09 January 2013 - 03:30 PM
Is there anything distinctive between the answers?

EDIT: Ok so I just typed in this http://api.wolframal...ormat=plaintext and I see what you mean (never used wolframalpha before)… maybe before looping to get the net answer insert a blank line into the table, that way they are separate.
Heracles421 #16
Posted 09 January 2013 - 03:32 PM
Is there anything distinctive between the answers?

Yea, for example, if you search about Steve Jobs, you get his full name, biography (date of birth, death, parents, etc) what things he made, how did things changed with all of that, and things like that
theoriginalbit #17
Posted 09 January 2013 - 03:34 PM
Yeh I noticed that, read the edit :)/>
Heracles421 #18
Posted 09 January 2013 - 03:41 PM
Maybe before looping to get the net answer insert a blank line into the table, that way they are separate.

I was thinking about this: If the line is bigger than the screen width, cut it, and repeat this process again. Once the line is smaller than the screen width (last line) add "\n" at the end of the line
theoriginalbit #19
Posted 09 January 2013 - 03:42 PM
That could work too.
Heracles421 #20
Posted 09 January 2013 - 03:48 PM
And another problem I've thought of, does lua detect words? Because if it doesn't, the line cut could slice a word in half (Wordmicide!)
theoriginalbit #21
Posted 09 January 2013 - 03:53 PM
there is a way to detect words using a regex pattern. let me just look it up in my string library…

ok so here is an example of how I used it:


function titleCase( str )
  local nStr, count = str:gsub("(%a)([%w_']*)", function(first, rest) return first:upper()..rest:lower() end )
  return nStr
end

obviously you wouldn't use it quite like this, you have to basically check if the word will fall outside the screen and if it does add a \n before it when adding into the table.
Heracles421 #22
Posted 09 January 2013 - 04:07 PM

function splitLine()
  length = string.len(text[index])
  swidth = term.getSize()
  local lineindex = 1
  if length > swidth then
    line[lineindex] = text[index]:gsub("(%a)([%w_']*)")
    lineindex = lineindex + 1
  end
end
Not sure if that'll work at all, but hopefully it's a good starting point
theoriginalbit #23
Posted 09 January 2013 - 04:10 PM
First thing that jumps out that would cause an issue is the declaration of the function. see fix:

-- declaration

function splitLine( text )
-- when calling 
splitLine( text[index] )
Heracles421 #24
Posted 09 January 2013 - 04:14 PM
First thing that jumps out that would cause an issue is the declaration of the function. see fix:

-- declaration

function splitLine( text )
-- when calling 
splitLine( text[index] )

Changed that before you posted this, as well as the table line being local :P/>
theoriginalbit #25
Posted 09 January 2013 - 04:39 PM
here is one I just wrote that I tested and works :)/> … if you need anything explained just ask :)/>

Spoiler


function splitLine( text )
local swidth = term.getSize()

local function split( str, regex )
local t = { }
local fpat = "(.-)"..regex
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end

local currLine = ""
local words = split( text, " " )
local t = {}
for i = 1, #words do
if #currLine + #words[i] + 1 > swidth then
table.insert( t, currLine )
currLine = words[i].." "
else
currLine = currLine..words[i].." "
end

if i == #words then table.insert( t, currLine ) end
end

return table.concat( t, "\n" )
end
Heracles421 #26
Posted 09 January 2013 - 04:52 PM
here is one I just wrote that I tested and works :)/>/>/> … if you need anything explained just ask :)/>/>/>

Spoiler


function splitLine( text )
local swidth = term.getSize()

local function split( str, regex )
local t = { }
local fpat = "(.-)"..regex
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end

local currLine = ""
local words = split( text, " " )
local t = {}
for i = 1, #words do
if #currLine + #words[i] + 1 > swidth then
table.insert( t, currLine )
currLine = words[i].." "
else
currLine = currLine..words[i].." "
end

if i == #words then table.insert( t, currLine ) end
end

return table.concat( t, "\n" )
end



I understand parts of it, not much :P/>
Seems like I don't know a lot yet
theoriginalbit #27
Posted 09 January 2013 - 05:02 PM

local function split( str, regex )
local t = { }
local fpat = "(.-)"..regex
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
This is a function that I have wrote several times before and just copy, pasted it from my "Extended String Library". Without going through too much detail the basics is, it searches through the string for the given pattern. And stores everything between it in a table. If you want me to explain this one just ask.


local words = split( text, " " )
so this means we split the text into words.


local t = {}
just a temp table.

Now the important bit

for i = 1, #words do
For every word that we have in the text do the following


if #currLine + #words[i] + 1 > swidth then
  table.insert( t, currLine )
  currLine = words[i].." "
else
  currLine = currLine..words[i].." "
end
If the length of our current line, plus the length of the word, plus 1 is larger than the screens width then insert the current line into our table and then start a new line.
If its not out of bounds, add the word to the line with a space


return table.concat( t, "\n" )
Return the contents of our table as a string where the separators between table elements is a new line.


Make sense now?
Edited on 09 January 2013 - 04:03 PM
Heracles421 #28
Posted 09 January 2013 - 05:14 PM
- snip -

Make sense now?

- snip -

Now I get it, all that code was confusing me, thanks for the explanation. Now, if I understood correctly, every line is stored in the table t, and I could use that to print the lines of the http answer?
theoriginalbit #29
Posted 09 January 2013 - 05:23 PM
oh thats right we were storing the return in a table… oops sorry… change the return to this

return t

then in your code you would do this…


local d = splitLine( theLineString )
for i = 1, #d do
table.insert( t, d[i] )
end
this will add each element in our line, to the main one ( thought there was an easier way but cant find it )

then for printing

term.setCursorPos( 1, startY + i )
for i = 1, #t do
  if startY + i > maxY then
	break
  end
  print( t[i] )
end
Heracles421 #30
Posted 09 January 2013 - 05:51 PM
Ok, I'll implement it and tell you if something went wrong