NCL 305: Updating the Screen

In the last lesson, we built a complete screen and showed it with one:

D.BLT

That works well when we're drawing the screen for the first time.

But suppose only one part changes.

Our status screen currently says:

STATUS:     READY

and we want it to say:

STATUS:     BUSY

We don't need to build the entire screen again.

We can update the field that changed.

Replacing a value

The status value begins at:

(15, 6)

So we can move there and write the new value:

D.CUR 15 6
D.CHR "BUSY"

Because D.CHR immediately refreshes the affected tiles, the new text appears immediately.

There's a problem.

READY is five characters long.

BUSY is only four.

Writing four characters doesn't erase the fifth character that was already there.

Instead of:

BUSY

we get:

BUSYY

The old Y is still there because we never replaced it.

Overwriting the whole field

Suppose we've reserved 10 character positions for the status.

One solution is to make every replacement exactly 10 characters long:

D.CUR 15 6
D.CHR "BUSY      "

The first four positions contain BUSY.

The remaining six spaces overwrite anything left from the previous value.

We've already learned how to pad strings, so we could do the same thing with a value stored in a string register.

This works, but it mixes two different things together.

The value is:

BUSY

The field happens to be 10 characters wide.

There is also another problem with our particular screen.

Restoring the background

In NCL 304, we gave our screen a coloured background:

D.COL #D.COL.BLUE #D.TXT.INVERT
D.FIL " "

The background is made from inverted blue spaces.

If we erase the end of an old value using ordinary foreground spaces, we aren't actually restoring the background.

Instead of thinking:

Erase the old text.

it's more useful to think:

Restore the field, then draw its new value.

First, let's make a string containing enough spaces to cover the complete field.

#blank s1

SMOVE #blank ""
SPADR #blank #blank 10 " "

#blank now contains 10 spaces.

Whenever we need to restore our 10-character status field, we can draw those spaces using the background style:

D.CUR 15 6
D.COL #D.COL.BLUE #D.TXT.INVERT
D.CHR #blank

Then we return to the foreground style, move back to the beginning of the field, and draw the new value:

D.COL #D.COL.WHITE #D.TXT.NORMAL
D.CUR 15 6
D.CHR #status

Together:

D.CUR 15 6
D.COL #D.COL.BLUE #D.TXT.INVERT
D.CHR #blank

D.COL #D.COL.WHITE #D.TXT.NORMAL
D.CUR 15 6
D.CHR #status

The first write restores all 10 character positions.

The second writes the new value over them.

The field has a width

There's an important difference between:

BUSY

and:

BUSY······

Our status value is only:

BUSY

So that's all we need to store:

SMOVE #status "BUSY"

We don't need to pad #status to 10 characters.

The background write already restored the complete field before we drew the new value.

#blank is 10 characters long because the field is 10 characters wide.

#status is four characters long because the value BUSY is four characters long.

Keeping those separate means our value doesn't need to contain extra spaces just because of where we happen to display it.

Updating several fields together

Sometimes several values change at the same time.

Suppose we want to change:

STATUS:     READY
MODE:       LOCAL

to:

STATUS:     BUSY
MODE:       REMOTE

If we want both changes to appear together, we can prepare them with D.TXT.

First the status:

D.CUR 15 6
D.COL #D.COL.BLUE #D.TXT.INVERT
D.TXT #blank

D.COL #D.COL.WHITE #D.TXT.NORMAL
D.CUR 15 6
D.TXT #status

Then the mode:

D.CUR 15 8
D.COL #D.COL.BLUE #D.TXT.INVERT
D.TXT #blank

D.COL #D.COL.WHITE #D.TXT.NORMAL
D.CUR 15 8
D.TXT #mode

Finally:

D.BLT

shows the completed changes together.

Again, neither #status nor #mode needs to be padded.

Each complete field was restored before its new value was written.

Keep an update together

For an immediate update, restore the field and draw the value with D.CHR:

D.CHR #blank
D.CHR #status

For a buffered update, do both with D.TXT, then refresh:

D.TXT #blank
D.TXT #status
D.BLT

Avoid restoring the complete field with D.TXT and then drawing a shorter value with D.CHR.

For example:

D.TXT #blank
D.CHR #status

D.TXT has restored the complete field in Display state, but hasn't refreshed it physically.

D.CHR immediately refreshes the tiles affected by the new value.

If the new value is shorter than the old one, some of the restored field may extend beyond those tiles and remain visibly unchanged until a later refresh.

Update Restore with Draw with Finish with
Immediate D.CHR D.CHR Nothing
Buffered D.TXT D.TXT D.BLT

In both cases, the basic operation is the same:

  1. restore the complete field;
  2. draw the new value.

Let the restore string do more

Our #blank string works, but updating a field still takes several instructions:

D.CUR 15 6
D.COL #D.COL.BLUE #D.TXT.INVERT
D.CHR #blank

D.COL #D.COL.WHITE #D.TXT.NORMAL
D.CUR 15 6
D.CHR #status

We've already learned enough about strings, control characters, and Display styling to make this cleaner.

Instead of storing only 10 spaces, we can build a string that performs the whole restoration.

Start with 10 spaces:

#restore s1

SMOVE #restore ""
SPADR #restore #restore 10 " "

Then extend it with 10 backspaces:

SPADR #restore #restore 20 "\b"

The string now contains:

[10 spaces][10 backspaces]

Writing it clears 10 positions, then moves the cursor back to where it started.

Now we can wrap it in style changes:

SJOIN #restore "\s12" #restore
SJOIN #restore #restore "\s0F"

The complete string is effectively:

[blue inverted style][10 spaces][10 backspaces][normal white style]

When the Display receives it, it:

  1. switches to the background style;
  2. restores the complete 10-character field;
  3. moves the cursor back to the beginning;
  4. restores the normal foreground style.

That means a complete immediate field update becomes:

D.CUR 15 6
D.CHR #restore
D.CHR #status

The restore string leaves the cursor and style ready for the value that follows.

We can use exactly the same idea for a buffered update:

D.CUR 15 6
D.TXT #restore
D.TXT #status
D.BLT

The field still has a width.

The value still doesn't need one.

We've simply packaged more of the field-restoration work into the string itself.

Try it

Take the status screen from NCL 304 and make the status field change after a key is pressed.

Try several values with different lengths:

READY
BUSY
OFFLINE
OK

Give the field enough width for the longest value.

First, update it using a plain 10-character #blank and the appropriate D.COL and D.CUR operations.

Then build a #restore string containing:

[background style][10 spaces][10 backspaces][foreground style]

and reduce the update to:

D.CUR 15 6
D.CHR #restore
D.CHR #status

Next, add a changing mode field:

LOCAL
REMOTE
AUTO

Try updating one field immediately with D.CHR.

Then change both fields using D.TXT, followed by one:

D.BLT

Notice that the same restore-and-draw pattern works in both cases.

When part of a screen changes, we don't need to rebuild everything around it.

We can reserve an area for a changing value, restore that field when necessary, and draw its new contents.

The field has a width. The value does not need to.

And because control characters and style changes can live inside strings, a restore string can do more than contain blank spaces: it can restore the field, return the cursor, and leave the Display ready for the value that follows.


So far, our program has decided when a field changes.

It restores the field, draws the new value, and leaves the rest of the screen alone.

In NCL 306: Changing a Setting, we'll put that same update technique under the user's control. The program will wait for Keyboard input, change a stored setting, and update the fields that represent it.