User Tools

Site Tools


Scrolling over a large canvas

by Richard Russell, September 2007

The code in this article requires BBC BASIC for Windows version 5.70a or later.

Sometimes it may not be possible to display the entirety of your program's User Interface at once in a window of a sensible size, especially when you need the program to run on PCs with a relatively low display resolution. One solution to this is to give the user the ability to scroll the window so that the region of interest is in view.

Although there are a number of ad-hoc ways in which this can be achieved, depending on precisely what needs to be displayed, each must be tailored to the particular circumstances and can be quite tricky to implement successfully. This article presents an alternative approach: a general-purpose method which should work for almost any situation.

BBC BASIC for Windows' display bitmap is 1920 x 1440 pixels and the code listed in this article is directly applicable to a 'canvas' (over which the user can scroll the output window) up to this size. If this isn't big enough you may be able to increase it using the method described in the Help documentation but beware of excessive memory usage.

The method will be illustrated by means of a practical example. You should be able to adapt it to your particular requirements fairly easily. Firstly, your program must include (at or near the beginning) some necessary initialisation:

        CanvasX% = 1920
        CanvasY% = 1440
        WindowX% = 640
        WindowY% = 480
        CharX% = 8
        CharY% = 16
        GWL_STYLE = -16
        WM_SIZE = 5
        WM_HSCROLL = &114
        WM_VSCROLL = &115
        DIM Move%(2)
        ON MOVE Move%()=@msg%,@wparam%,@lparam%:PROCmove(Move%(0),Move%(1),Move%(2)):RETURN
        SYS "GetWindowLong", @hwnd%, GWL_STYLE TO ws%
        SYS "SetWindowLong", @hwnd%, GWL_STYLE, ws% OR &300000
        VDU 23,22,WindowX%;WindowY%;CharX%,CharY%,16,128
        ORIGIN 0,2*(@vdu%!212-CanvasY%)
        VDU 24,0;0;CanvasX%*2-2;CanvasY%*2-2;
        VDU 28,0,CanvasY%/CharY%-1,CanvasX%/CharX%-1,0

For the purposes of the example the 'canvas' has here been set to the normal maximum size, 1920 x 1440 pixels, and the output window has been set (initially) to 640 x 480 pixels. CharX% and CharY% are the width and height, in pixels, of the text characters. In practice you should set CanvasX% and CanvasY% to whatever dimensions are appropriate for your application. The user can scroll the output window over this range, and can resize the window in the usual ways (e.g. dragging an edge or corner).

Once the initialisation is complete you can proceed to display whatever contents of the user interface you wish, up to the full size of the 'canvas'. In this example we will simply display a short piece of text, a BMP image and a pair of diagonal lines:

        *DISPLAY "C:\Windows\Web\Wallpaper\Bliss.BMP"
        PRINT "Scroll down to see the picture"
        LINE 0,0,CanvasX%*2-2,CanvasY%*2-2
        LINE 0,CanvasY%*2-2,CanvasX%*2-2,0

Because you will be drawing outside the area occupied by the visible output window you should take note of the article Drawing outside the window, in particular in respect of the graphics origin. The initialisation code above sets the graphics origin to the bottom-left corner of the canvas, and sets both the graphics viewport (VDU 24) and text viewport (VDU 28) to the full size of the canvas.

Now we can enter an 'idle' loop during which the user can scroll the window:

          WAIT 1

Of course in a practical program it is likely you will want to do something more useful here!

Finally, here is the procedure which contains the 'magic' to make all this work:

        DEF PROCmove(msg%,wp%,lp%)
        PRIVATE sih{}, siv{}
        DIM sih{cbSize%, fMask%, nMin%, nMax%, nPage%, nPos%, nTrackPos%}
        DIM siv{cbSize%, fMask%, nMin%, nMax%, nPage%, nPos%, nTrackPos%}
        sih.cbSize% = DIM(sih{})
        siv.cbSize% = DIM(siv{})
        sih.fMask% = 7
        siv.fMask% = 7
        sih.nMax% = CanvasX%
        siv.nMax% = CanvasY%
        CASE msg% OF
          WHEN WM_SIZE:
            sih.nPage% = lp% AND &FFFF
            siv.nPage% = lp% >>> 16
          WHEN WM_HSCROLL:
            CASE wp% AND &FFFF OF
              WHEN 0: sih.nPos% -= 1
              WHEN 1: sih.nPos% += 1
              WHEN 2: sih.nPos% -= sih.nPage%
              WHEN 3: sih.nPos% += sih.nPage%
              WHEN 5: sih.nPos% = wp% >> 16
          WHEN WM_VSCROLL:
            CASE wp% AND &FFFF OF
              WHEN 0: siv.nPos% -= 1
              WHEN 1: siv.nPos% += 1
              WHEN 2: siv.nPos% -= siv.nPage%
              WHEN 3: siv.nPos% += siv.nPage%
              WHEN 5: siv.nPos% = wp% >> 16
        IF sih.nPos% > sih.nMax%-sih.nPage% sih.nPos% = sih.nMax%-sih.nPage%
        IF siv.nPos% > siv.nMax%-siv.nPage% siv.nPos% = siv.nMax%-siv.nPage%
        IF sih.nPos% < sih.nMin% sih.nPos% = sih.nMin%
        IF siv.nPos% < siv.nMin% siv.nPos% = siv.nMin%
        SYS "SetScrollInfo", @hwnd%, 0, sih{}, 1
        SYS "SetScrollInfo", @hwnd%, 1, siv{}, 1
        @ox% = sih.nPos%
        @oy% = siv.nPos%
        SYS "InvalidateRect", @hwnd%, 0, 0

Note that the code in this article is not compatible with SPRITELIB.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
scrolling_20over_20a_20large_20canvas.txt · Last modified: 2018/04/17 14:49 by richardrussell