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:
- restore the complete field;
- 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:
- switches to the background style;
- restores the complete 10-character field;
- moves the cursor back to the beginning;
- 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.