Text Wrapping in roImageCanvas

Hi all,

I’m using an roImageCanvas to build a grid of rectangles that will make a grid for TV listings.

Basically how I understand it is, you position your text on the screen via a TargetRect with an X, Y position and a length and width.

The width of the TargetRect handles the wrapping great. If the text is too wide for the TargetRect, it wraps to the next line.

What I’m trying to figure out is how to get it to stop displaying text if there are too many lines. It’s not doing anything about the height of the TargetRect. It will just keep printing the text, albeit the correct width, on several lines well beneath the TargetRect.

Is there any way to solve this issue without complex coding, text width analysis, etc?

Here’s my code which put’s a red rectangle on the screen the same size as the Text’s TargetRect. The text printed goes outside of the bottom of the TargetRect because it’s too long.


function Main()
    
    canvas = CreateObject("roImageCanvas")
    
    canvas.SetLayer(0, [
    {
        Color: "#FF0000",
        TargetRect: {x: 400, y: 300, h: 100, w: 400}
    },
    {
        Text: "Hello this is a long sentence about something I will see on TV someday and this is a continuation of that sentence which will wrap beneath the target rectangle",
        TextAttrs: {color: "#FFFFFF", valign: "Top", halign: "Left"},
        TargetRect: {x: 400, y: 300, h: 100, w: 400} 
    }
    ])
    
    port = CreateObject("roMessagePort")
    canvas.SetMessagePort(port)
    canvas.Show()
    while true
        event = Wait(0, port)
        if (event.IsRemoteKeyPressed())
            index = event.GetIndex()
            if (index = 6) ' Press OK to exit
                exit while
            end if
        end if
    end while
    
end function

Here’s a bit of code I use to cut off something that’s too long:

IF Len(adjusters) > 30 THEN adjusters = Left(adjusters, 28) +"..." ' If there are many stat adjusters, cut this off short

I had thought about doing something like that, but it doesn’t take into account the width of the font. Like 30 capital W’s versus 30 lower case i’s.

I think I’m going to explore the EPGGrid of the scene graph API to implement my project instead of ImageCanvas.

I posted some code to do wrapping, to which height counting could easily be added: viewtopic.php?f=34&t=64824&p=415848#p415848

However this probably only works well with roScreen; I don’t think it will work correctly with ImageCanvas.

–Mark

That is a very useful function that I currently use and have used quite a bit. Thanks for sharing that. It saved me a great deal of time not having to write it myself. Just be aware, and I believe it was mentioned somewhere, perhaps by yourself, that it does not address strings without whitespace that happen to be longer than “width”. Such as a long URL that you wanted to display in a dialog. If it is longer than the width and the function cannot find whitespace to break it up then it will go into an endless loop. I have made adjustments to it myself to accommodate those types of strings pretty much the same way as the Scene Graph components handle it, at least according to the Scene Graph documentation. But for normal strings it is a great piece of code.

If you just want to prevent the text from overlapping other UI elements, you could redraw the background in that section at a higher layer (using SourceRect to slice out the area you need to repeat). The text would still overrun your TargetRect, but it’d be hidden behind the background slice.

“ttown253” wrote:
What I’m trying to figure out is how to get it to stop displaying text if there are too many lines. It’s not doing anything about the height of the TargetRect. It will just keep printing the text, albeit the correct width, on several lines well beneath the TargetRect.

Is there any way to solve this issue without complex coding, text width analysis, etc
If you do decide to use an roImageCanvas, compute the height for your text rectangle by taking the number of lines of text you want in each rectangle multiplied by GetOneLineHeight(). By drawing another text rectangle immediately adjacent to the one with overflowing text, as long as its background is completely filled, you will over-write any overflow text from the previous text rectangle, as demonstrated in the following code (which does require you to use a custom font):

function Main()

    fontFile = "pkg:/fonts/Font.ttf"        ' From: http://www.dafont.com/liberation-sans.font
    fontName = "Liberation Sans"
    fontSize = 24
    
    fr = CreateObject ("roFontRegistry")
    fr.Register (fontFile)

    fontObject = fr.GetFont (fontName, fontSize, false, false)
    fontString = fr.Get (fontName, fontSize, false, false)

    lineH = fontObject.GetOneLineHeight ()
    nLines = 3
    rectH = nLines * lineH

    canvas = CreateObject("roImageCanvas")

    canvas.SetLayer(0, [
    {
        Color: "#FF0000",
        TargetRect: {x: 200, y: 200, h: rectH, w: 400}
    },
    {
        Text: "Hello this is a long sentence about something I will see on TV someday and this is a continuation of that sentence which will wrap beneath the target rectangle",
        TextAttrs: {font: fontString, color: "#FFFFFF", valign: "Top", halign: "Left"},
        TargetRect: {x: 200, y: 200, h: rectH, w: 400}
    },
    {
        ' The next grid element over-writes any overflow from the previous element
        Color: "#00FF00",
        TargetRect: {x: 200, y: 200 + rectH, h: rectH, w: 400}
    },
    {
        Text: "This is the next sentence",
        TextAttrs: {font: fontString, color: "#FFFFFF", valign: "Top", halign: "Left"},
        TargetRect: {x: 200, y: 200 + rectH, h: rectH, w: 400}
    }
    ])

    port = CreateObject("roMessagePort")
    canvas.SetMessagePort(port)
    canvas.Show()
    while true
        event = Wait(0, port)
        if (event.IsRemoteKeyPressed())
            index = event.GetIndex()
            if (index = 6) ' Press OK to exit
                exit while
            end if
        end if
    end while

end function