Text New Line Help

I am trying to get the text to be drawn right above each line. When I do textwrapWidth() or any next line "
" it is pushing the text up. If someone would guide me in the right direction that would be appreciated! :slight_smile:

-- Help

-- Use this function to perform your initial setup
function setup()
    tex = "testing 123 \
 testing 321"
end

-- This function gets called once every frame
function draw()
    
    NotesScreen()
    
end

function NotesScreen()
    background(255, 255, 255, 255)
            stroke(0, 0, 0, 255)
            strokeWidth(3)
            line( WIDTH/1.25, 0, WIDTH/1.25, HEIGHT )
            line( 0, (HEIGHT/24)*21.1, WIDTH/1.25, (HEIGHT/24)*21.1)
                strokeWidth(1)
        
            for n = 1, 20 do
            local NotesLine = line( 0, (HEIGHT/24)*n, WIDTH/1.25, (HEIGHT/24)*n)
            end
            textMode(CORNER)
            textWrapWidth(WIDTH/2)
            fontSize(20)
            local h = (HEIGHT/24)*20
            local sentence = text(tex, 0, h)
            textMode(CENTER)
    if h > (HEIGHT/24)*20 then
        h = (HEIGHT/24)*20
    end
           
end

function keyboard(k)
    if k==BACKSPACE then
        ans=string.sub(ans,1,string.len(ans)-1)
        return
    end
    
    tex = tex..k
    
end

Here you go, fixed it. Because textMode(CORNER) draws from the bottom-left of the text box, you need to move the y coordinate down the screen depending on the number of lines. However, it could be tricky to precisely line up the text with the lines if you are just printing it as a block of text. It could depend on things like the font kerning. Dunno, I didn’t bother testing that. Plus, the line height changes when you switch orientation as you define it as a proportion of height. An easier option if you want to precisely control the printing position of each line, is to store each line as a separate string in a table. Then you can print them exactly where you want.

I also made line width and height constant (with a function to recalculate them when the orientation changes), because you were doing the same calculation over and over again which is a) a waste of resources b) hard to read, and c) annoying if you decide to change the values.

I even threw in a cursor for you. Update: line-wrapping and delete key fixed

-- Help

-- Use this function to perform your initial setup
function setup()
    tex = {"testing 123","testing 321"} --each line stored in a separate string, in order to precisely control printing location
    showKeyboard()
    width, height = WIDTH/1.25, HEIGHT/24 --made these constants to prevent same calculation being done over and over, and for readability
end

-- This function gets called once every frame
function draw()
    
    NotesScreen()
    
end

function orientationChanged()
    width, height = WIDTH/1.25, HEIGHT/24 --recalculate
end

function NotesScreen()
    background(255, 255, 255, 255)
    stroke(0, 0, 0, 255)
    strokeWidth(3)
    line( width, 0, width, HEIGHT )
    line( 0, height*21.1, width, height*21.1)
    strokeWidth(1)
    
    for n = 1, 20 do
        local NotesLine = line( 0, height*n, width, height*n)
    end
    textMode(CORNER)
   -- textWrapWidth(WIDTH/2) --we're doing our own text wrapping
    fontSize(20)
    
    local cursorSpeed = 4
    local cursorLen = (ElapsedTime*cursorSpeed%2)//1 --a number that regularly alternates between 0 and 1 
    local cursor = ""
    for i,v in ipairs(tex) do --go through the table
        if i==#tex then cursor=string.rep("\\u{25af}", cursorLen)..string.rep("\\u{25ae}", 1-cursorLen) end --cursor symbol..empty cursor symbol
        text(v..cursor, 0, height*(20-i)) --moving the y position down the screen
    end
    textMode(CENTER)
    --[[ --not sure what this is meant to do
    if h > height*20 then
        h = height*20
    end
      ]]
    
end

function keyboard(k)
    if k==BACKSPACE then
        if tex[#tex]=="" then
            if #tex>1 then table.remove(tex) end --remove last line
        else
            tex[#tex]=string.sub(tex[#tex],1,-2) --string.len(ans)-1 no need for string.len, just use a negative number
        end
    elseif k==RETURN then
        tex[#tex+1]="" --add a new item to the table
    else   
        tex[#tex] = tex[#tex]..k 
        --text wrap
        local w,_ = textSize(tex[#tex])
        if w>WIDTH/2 then
            tex[#tex+1]=""
        end
    end
end

Also, I would turn off textWrap and add hard new lines when the line got to the desired length.

Ok, I fixed text-wrapping and delete in the above code

@yojimbo2000

An incredible thanks! You explained everything and I needed to make a cursor at some point!! :smiley: the only problem is if it back spaces back all the way it won’t let me type, but I’ll take a look at that after work. Thank you! And cheers!

No problem. I fixed the backspace bug, now you can’t delete past the start of the text.

@yojimbo2000 thank you again! I’m going to learn a lot from this! It’s much appreciated!!!

@FearMe2142 looking at this again, you can in fact accurately determine the height of the text you are going to print. You need to grab a table of fontMetrics and then add together ascent and descent to get the total line height. This means that you can, if you wish, keep the text together in a continuous string, which does simplify the code slightly. In the code below I’ve also added typewriter scrolling, so that what you’ve written doesn’t disappear off the edge of the screen or beneath the keyboard. I was also able to use ascent and descent to draw two lines, a thicker one for the text to “sit” on, and a paler one as the separator between each line. One disadvantage of this method compared with the above is that Codea doesn’t tell us where, horizontally, the block of text ends, so our cursor options are limited to text-based cursors, such as the below (so if you wanted, say, a cursor that was a different colour to the text, that’d take a rethink).

Edit: tidied up the code a bit

-- Notepad
-- by Yojimbo2000

displayMode(OVERLAY)

function setup()
    inkey=[[Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget hendrerit ligula. Quisque vitae mauris aliquet risus efficitur hendrerit at nec quam. Nulla ut sollicitudin dolor, ut interdum nulla. Quisque malesuada fermentum erat vitae mattis. Suspendisse vitae cursus purus, sit amet dictum risus.
    
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget hendrerit ligula. Quisque vitae mauris aliquet risus efficitur hendrerit at nec quam. Nulla ut sollicitudin dolor, ut interdum nulla. Quisque malesuada fermentum erat vitae mattis. Suspendisse vitae cursus purus, sit amet dictum risus.]]
    
    font("Zapfino")
    fill(31, 31, 88, 255)
    fontSize(20)
    strokeWidth(1)
    textMode(CORNER) --necessary to stop cursor displacing text
    showKeyboard()
    metrics(CurrentOrientation)
    
    --measure the font
    local fon = fontMetrics()
    for k,v in pairs(fon) do
        print(k,v)
    end  
    ascent = fon.ascent
    descent = fon.descent
    size = ascent + descent
    screeny, targety = 0,0
end

function metrics(orientation)
    width = WIDTH*0.8
    textWrapWidth(width)
    border = HEIGHT - 50
    pos = vec2((WIDTH - width)*0.5, HEIGHT*0.5)
    if orientation==PORTRAIT or orientation==PORTRAIT_UPSIDE_DOWN then
        typeWriter = HEIGHT * 0.3 --the height at which we want the last line to stay at, to prevent what we're typing disappearing off the page, or being covered by the keyboard
    else
         typeWriter = HEIGHT * 0.45 --keyboard takes up more space in landscape
    end
end

function draw()
    background(223, 215, 165, 255)
    --cursor
    local cursorSpeed = 4
    local cursorLen = (ElapsedTime*cursorSpeed%2)//1 --a number that regularly alternates between 0 and 1 
    local cursor=string.rep("\\u{25ae}", cursorLen)..string.rep("\\u{25af}", 1-cursorLen) --cursor symbol..empty cursor symbol. The empty symbol is necessary otherwise the cursor blinking will push a word past the word wrap limit if you're at the end of a line. One of the disadvantages of a text-based cursor
    
    --Establish y pos of text and scroll the screen/ move the camera accordingly
    local w,h = textSize(inkey..cursor)
    pos.y = border - h
    targety = math.max(0, typeWriter-pos.y)
    screeny = screeny + (targety - screeny) * 0.1
    translate(0,screeny)
    
    --draw lines
    for y = border-ascent, pos.y, - size do
        stroke(39, 26, 26, 128)
        line(pos.x-10,y,pos.x+width+10,y) --line that text "sits on"
        stroke(68, 50, 50, 48)
        line(pos.x-10,y-descent,pos.x+width+10,y-descent) --separator between ascendors and descendors
    end

    text(inkey..cursor, pos.x, pos.y)  
end

--The three built-in user-input functions that Codea ships with:

function keyboard(key)
    if key==BACKSPACE then
        inkey=string.sub(inkey, 1, -2)
    else
        inkey=inkey..key
    end
end

function touched(t)
    if t.state==BEGAN then --toggle keyboard
        if isKeyboardShowing() then hideKeyboard() else showKeyboard() end
    end
end

function orientationChanged(orientation)
    metrics(orientation) --re-measure width, height
end

An image:

notepad

awsome!

Thanks! @Simeon it would be really awesome if the text API could tell us at what x position a wrapped block of text stops printing (ie to add a proper cursor to the above). Perhaps it could be a third value that textSize returns? Eg width, height, endWidth = textSize("A block of text that exceeds the wrap-width")

One disadvantage of printing the text in one big block though is there does seem to be a limit: if the height of the printed text is more than 1024, it doesn’t seem to display (I would have expected the limit to be 2048, the limit of texture sizes in OpenGLES 2.0). So if you’re wanting long form text display, you probably need to parse the string by paragraph. Have a look at my markdown parser.

@yojimbo2000 thanks a lot yojimbo! I’m actually planning on needing each complete sentence in a table as I’m going to want it to be used somewhere else. I did have 2 questions on how I might make it so that I could have a different Fontsize for the table strings.


If nstn == 1
    Then tex[#tex] 
         Fontsize(20)

Then the next table would be Fontsize (30)

If Nstn > 1

The last thing I’ve been trying to accomplish is when the return key is hit, if the current table has a Roman numeral from any of the roman table then automatically add the next one in the table;


Roman, RomanNumeral ={ "I.", "II." "III." }, 1

if tex[Roman] then -- if it contains a roman numeral
            print("passed")
            tex[#tex+1] = "    "..Roman[RomanNumeral].."."
            RomanNumeral = RomanNumeral + 1
        else
        tex[#tex+1]=""        
            print(tex)
        end

Not sure how to make the current table check to see if it has a value in Roman. I’ve looked at some Luna table examples, but with no luck so far

how beautiful the font is! i think of my good English handwriting

very very good?I like it?

@FearMe2142 if you want to find things in a string, use string.find, string.match, string.sub. Checking for Roman numerals would be a pain though, and something the user is unlikely to discover by themselves. A much better approach I think would be to check for regular numerals, and then substitute them for the corresponding Roman numeral. In that case, one of the sub functions could be used to strip out the numeral and replace it.

eg.

-- Roman Numerals

function setup()
    roman = {"I", "II", "III", "IV", "V","VI","VII","VIII","IX","X"}
    inkey=[[
    
1. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
2. Etiam eget hendrerit ligula. 
3. Quisque vitae mauris aliquet risus efficitur hendrerit at nec quam. 
4. Nulla ut sollicitudin dolor, ut interdum nulla. 
5. Quisque malesuada fermentum erat vitae mattis. 
6. Suspendisse vitae cursus purus, sit amet dictum risus.
7. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
8. Etiam eget hendrerit ligula. 
9. Quisque vitae mauris aliquet risus efficitur hendrerit at nec quam. 
10. Nulla ut sollicitudin dolor, ut interdum nulla. Quisque malesuada fermentum erat vitae mattis. Suspendisse vitae cursus purus, sit amet dictum risus.
    ]]
    test = string.gsub(inkey, "\
%s-(%d+)%.%s-", numeralToRoman) 
    --[[
    pattern breakdown
    "\
%s-(%d+)%.%s-" matches:
        \
  newline, followed by:
        %s-  zero or more white space characters
        (%d+)  one or more digits, () = the capture passed to the function
        %.  a literal full-stop, % = escape a magic character
        %s-  zero or more white space characters
      ]]
    
    print(test)
end

function numeralToRoman(capture) --only does 1-10, could get tricky when the number gets higher
    return "\
"..roman[tonumber(capture)]..". "
end