[Oberon] SystemV - Viewer Scroll

Andreas Pirklbauer andreas_pirklbauer at yahoo.com
Fri Mar 22 22:26:37 CET 2019

    > My quick hack was only applicable to Files.
    > Assuming you have a frame, initialized a Reader "R" already
    > and want to set this Reader back by one position.
    >    Texts.OpenReader(R, f.text, Texts.Pos(R)-1);
    >    Texts.Read(R, ch)
    > Jörg

Hi Jörg,

just in case ... you are thinking of actually implementing this kind
of “read backwards” operation, you’ll notice that the resulting code for
text scrolling will be fast enough (i.e. avoids flickering) *only* if you
implement it directly *within* modules Files and Texts (been there,
done that..). But if you implement it outside Files and Texts, i.e.
write something along the lines of:

  PROCEDURE ReadBack (T: Texts.Text; VAR R: Texts.Reader; VAR ch: CHAR; VAR bof: BOOLEAN);
    VAR pos: INTEGER;
  BEGIN pos := Texts.Pos(R);
    IF pos > 0 THEN bof := FALSE;
      Texts.OpenReader(R, T, pos-1); Texts.Read(R, ch); Texts.OpenReader(R, T, pos-1)
    ELSE ch := 0X; bof := TRUE
  END ReadBack;

then text scrolling will simply be too slow, when e.g. used as in:

 PROCEDURE Scroll* (F: Frame; dY: INTEGER);   (*scroll displayed text dY pixels up or down*)
    VAR R: Texts.Reader; L, L0: Line; bof: BOOLEAN;
      org: LONGINT; dy: INTEGER;
  BEGIN (*dY # 0*)
    IF F.trailer.next # F.trailer THEN
      IF dY < 0 THEN  (*move backwards*)
          dY := -dY; org := F.org; Validate(F.text, org);
          IF org > 0 THEN NewLine(F, L); dy := 0;
              Texts.OpenReader(R, F.text, org-1); ReadBack(F.text, R, nextCh, bof);
              ReadLineBack(L, F.text, R, bof);
              dy := dy + L.lsp; org := org - L.len;
              (*...code that sets up line list to be inserted ...*)
            UNTIL bof OR (dy + F.voff >= dY);
            ScrollDown(F, org, F.voff + dy - dY, dY)
      ELSIF dY > 0 THEN  (*move forward*)
  END Scroll;


 PROCEDURE ReadLineBack (L: Line; T: Texts.Text; VAR R: Texts.Reader; VAR bof: BOOLEAN); (*pass 1*)
    VAR maxY, minY, len: INTEGER;
  BEGIN maxY := asr; minY := -dsr; len := 0;
    WHILE ~bof & (nextCh # CR) DO
      IF R.fnt.maxY > maxY THEN maxY := R.fnt.maxY END;
      IF R.fnt.minY < minY THEN minY := R.fnt.minY END;
      INC(len); ReadBack(T, R, nextCh, bof)
    L.len := len + 1; L.asr := maxY; L.dsr := -minY; L.lsp := maxY - minY;
    L.eot := FALSE
  END ReadLineBack;

So if you want fast enough code for *completely smooth* and flicker-free
text scrolling, there are two options:

Option #1. Modify Files and Texts to add Files.ReadBack and Texts.ReadBack

Option #2. Find a faster solution than the “natural” code outlined above.

I have done both. In the end I decided to use option #2, as I wanted to leave
modules Files and Texts untouched. HOWEVER - and unfortunately - I admit
that I found no better solution than to simply “guess” the right position N times
until it is “right” (if a good heuristic is chosen, N = 1 or 2 in most cases though)
and then just read *forward* from there.

This makes the code more complex, but also faster (at least it eliminates flickering):

  PROCEDURE Scroll (F: Frame; dY: INTEGER); (*scroll displayed text dY pixels up or down*)
    CONST P = 100; len = 75; (*assumed average line length*)
    VAR R: Texts.Reader; L, L0: Line; done: BOOLEAN;
  BEGIN (*dY # 0*)
    IF F.trailer.next # F.trailer THEN
      IF dY < 0 THEN dY := -dY;
        IF dY <= F.voff THEN ScrollDown(F, F.org, F.voff - dY, dY)
        ELSE NewLine(F, L); done := FALSE;
          q := Max(F.org - len*((dY + lsp - F.voff - 1) DIV lsp), 0);  (*first guess*)
          REPEAT org := q; Validate(F.text, org); k := 0; dy := 0;
            Texts.OpenReader(R, F.text, org); Texts.Read(R, nextCh);
            WHILE (org < F.org) & (k < P) DO PrepareLine(L, R);
              dy := dy + L.lsp; p[k] := org; d[k] := L.lsp; org := org + L.len; INC(k)
            IF org < F.org THEN q := p[1]  (*next guess forward*)
            ELSIF q = 0 THEN (*reached beginning of text*) done := TRUE;
              IF dy + F.voff > 0 THEN ScrollDown(F, 0, 0, dy + F.voff) END
            ELSIF (k = 0) OR (dy + F.voff < dY) THEN q := Max(q - len, 0)  (*next guess backward*)
            ELSE (*found, now scroll*) k := 0; done := TRUE;
              WHILE dy - d[k] + F.voff >= dY DO dy := dy - d[k]; INC(k) END;
              ScrollDown(F, p[k], F.voff + dy - dY, dY)
          UNTIL done
      ELSIF dY > 0 THEN
  END Scroll;

If someone has a more elegant (and equally fast or even faster)
solution I am a happy taker.

PS: When it is done inside Files and Texts, scrolling is only
marginally faster than the above code...


More information about the Oberon mailing list