Sudden problem with scrolling :(

I’ve just come back to Xojo after a year away and would like to get back into working on a project I was writing a while ago. I’ve downloaded the latest version, made the changes required to work around the lack of canvas .graphics support, and now the program runs but with a weird problem I am at a loss to explain. There seems to be some sort of optimisation in the language which, although clearly great if you want it, is giving me a headache.

The program I’m working on is, essentially, an old school text adventure with in-line static graphics. The player types commands and the responses scroll up the screen. The text currently displayed on the screen is held in an array, the length of which is equal to the number of lines of text on the screen. The canvas paint method simply does a for…next loop to display each line of the array on the screen, top to bottom. This works fine.

This is where the problem lies. I have a method called DrawText(text). This simply removes the top item in the array (the first line of text on the screen), adds the passed text to the last item in the array, and then calls Canvas1.Refresh(). This used to work - every time a new line of text is added to the array, the screen refreshes and you see the text scrolling up the screen old school style. What is happening now is that Xojo seems to ignore the calls to refresh the canvas, even if I invalidate it, and instead of the text scrolling up the screen there is simply a pause and it all appears at once. This is obviously completely useless in a text adventure.

Even worse, there is supposed to be a typewriter mode where every character of every line of text is drawn one at a time like a typewriter is typing the text on the screen - so a screen refresh is required for every letter displayed, but all that happens now when typewriter mode is turned on is that there is a massive delay while nothing happens and presumably all the screen updates are being cached behind the scenes, and then everything appears at once.

What’s going on? How do I get my scrolling text back?

I would say that since you said you" made the changes required to work around the lack of canvas .graphics support," that the manner in which you did so was not the most optimal.

FYI… Refresh happens immediately (and should be used rarely and with caution). Invalidate happens at the end of a run cycle (and will happen ONCE regardless of how many times it is called during the run cycle). Both cause the PAINT event to be fired…
The Paint Event where all drawing and other graphic operations should take place either directly or indirectly

The only place I’d used the .graphics method was when drawing some overlaid icons which was fairly simple to change, the main canvas has always just used the .paint method, so no problems there.

What you say is precisely the point - that’s what should happen but it doesn’t seem to want to. I’m on a Mac, by the way, if that’s relevant (I doubt it). Refresh should happen immediately, and I’m essentially doing this (pseudo code):

[code]Function DrawText(text)
For a = 1 to length of array
move array elements 2 to length(array) up one
add text to array(length of array)
Canvas1.Refresh()
Next a

Canvas1.Paint method:
For a=1 to length(array)
draw array(a) at appropriate place on screen
next a
return[/code]

Since .Refresh is called every time the array is updated, and .Paint paints the latest version of the array, the text should be seen to scroll. I don’t get why it doesn’t.

Unfortunately, you were relying on old computers being slow to animate. To animate anything with modern technology you will need to implement a frame rate controller, draw each frame, display it on screen for the desired frame time, and then draw the next.

not saying this will solve your problem… but perhaps

Function DrawText(text)
if array.ubound>=0 then array.remove(0)
array.append newText
Canvas1.Refresh()

no need to refer to any array locations expect to remove the top of stack

and please use the forums code tags in the future

Is that because they’ve updated Xojo from 32 to 64 bit? When I compile my app with an old version of Xojo from last year it works but the Mac says my app is not optimised which it does for 32 bit apps. I always assumed Xojo had been 64 bit for years. Any idea how I would create a delay like this? I’ve tried using App.SleepCurrentThread(100) or some such but that does nothing, presumably because it is still queuing up refreshes in the background. It seems like one of the most annoying things about modern programming languages that there no longer ever seems to be a Pause function that just pauses the whole program for x amount of time. This issue obviously doesn’t stop me continuing development, but without a fix the game will end up being virtually unplayable as the player’s eyes have to spend all their time trying to work out what they’ve already read and where the new text begins!

I’m not certain if you can get execution time based animation to work in 32 bit, but implementing a framerate controller has always been the “correct” way to do animation.

You will need to use a timer to handle the timing aspects. The idea is that you don’t want to lock up the program, it needs to be free to display the frame after drawing it. Things that pause execution are not going to work (or if they do, not well).

There’s an example project for animation included with Xojo (Xojo/Example Projects/Graphics and Multimedia/Animation/Animation.xojo_binary_project) but the way it’s implemented doesn’t apply to every animation use case.

Not really
32 bit apps are, pretty soon, a dead thing on macOS
Newer versions of macOS already warn you about this
The reason is they link against a long deprecated set of API’s known as Carbon - which Apple is slowly but surely removing
And these old API’s let you do some things that are not safe in newer API’s (one reason Apple is getting rid of them)

64 bit apps link against the newer version of macOS API’s which has different requirements and which is probably what causes the difference

here is a crude example that scrolls text quite nicely… it is not how I’d do it in a production app, but it does illustrate the concepts

Note : no timers required.

  • create a project with a canvas and a button

CANVAS

Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
  // decide if we have overflowed or not
  Dim th As Double=g.TextHeight
  Dim h  As Double = th*myarray.ubound
  Dim i  As Integer
  Dim y As Integer
  While h>g.height 
    myArray.remove 0
    h=h-th
  Wend
  y=g.TextAscent
  For i=0 To myArray.ubound
    g.drawstring myArray(i),10,y
    y=y+g.TextHeight
  Next i
End Sub

METHOD

Sub AddText(msg as string)
  myArray.append(msg)
  canvas1.Invalidate
End Sub
Private Property myArray(-1) as string

BUTTON

Sub Action() Handles Action
// obviously replace this with something meaningful
  addtext "aldksfjalsdkfjas;dflk"+str(Ticks)
End Sub

Thanks all for your help - I’ll be having a play with all this later when time permits and my head isn’t threatening to explode on me :slight_smile:

Just an interesting observation:

When I said that the array holds the text on the screen, I was oversimplifying slightly to make it easier to understand. It actually holds the entire text of the game so far and only the last (screen rows) part of the array is displayed. This allows the player to use the arrow keys to scroll back through the text so far at any point - the game just changes the offset within the array to match the point at which the player has scrolled back to and calls a .Refresh(). This works. When the user holds down the UP arrow key, the text happily scrolls back through the text exactly as expected, presumably because it’s technically only doing one screen update and then waiting for a keypress even if the player is holding down the arrow key. Hmmm.

[quote=439131:@Dave S]here is a crude example that scrolls text quite nicely… it is not how I’d do it in a production app, but it does illustrate the concepts

Note : no timers required.

  • create a project with a canvas and a button

CANVAS

Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
  // decide if we have overflowed or not
  Dim th As Double=g.TextHeight
  Dim h  As Double = th*myarray.ubound
  Dim i  As Integer
  Dim y As Integer
  While h>g.height 
    myArray.remove 0
    h=h-th
  Wend
  y=g.TextAscent
  For i=0 To myArray.ubound
    g.drawstring myArray(i),10,y
    y=y+g.TextHeight
  Next i
End Sub

METHOD

Sub AddText(msg as string)
  myArray.append(msg)
  canvas1.Invalidate
End Sub
Private Property myArray(-1) as string

BUTTON

Sub Action() Handles Action // obviously replace this with something meaningful addtext "aldksfjalsdkfjas;dflk"+str(Ticks) End Sub [/quote]

I think the issue is mainly that the text is generated programmatically. What you’re doing here is adding text in response to the user pressing a button, which adds a user generated delay. That’s sort of what happens in my situation above where the user can use the arrow key to scroll back. I suspect that if you had the button display several lines of text such as:

for a=1 to 20 AddText("This is a line of text") next a

you would encounter the same problem as me!