=====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 [[http://www.bbcbasic.co.uk/bbcwin/manual/bbcwini.html#hint2|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%20outside%20the%20window|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:
REPEAT
WAIT 1
UNTIL FALSE
END
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
ENDCASE
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
ENDCASE
ENDCASE
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
*REFRESH
ENDPROC
Note that the code in this article is not compatible with SPRITELIB.