Here's what I've done. The only line you need to change is 587, where it asks for an image to use as the background for the ScrollAreaGadget. You should use a small image that can be tiled.
I'm pretty sure this code covers all the functionality of Windows scrollbars.
The only problem is dealing with clicking in the bar (not the slider, but on the bar itself). I decided that this should make the slider centre on the mouse position, but it doesn't work well at the extreme ends of the scrollbar.
Nonetheless, I hope people in the future find this code useful.
Any suggestions or improvements would be most welcome.
Code: Select all
Global exitbutton.l
;- COSMETIC FUNCTIONS
Procedure DrawBox(x.l,y.l,w.l,h.l,colour.l)
LineXY(x,y,x+w,y,colour) ; top
LineXY(x,y,x,y+h,colour) ; left
LineXY(x,y+h,x+w,y+h,colour) ; bottom
LineXY(x+w,y,x+w,y+h,colour) ; right
EndProcedure
Procedure.l SetWindowBrush(window.l,image.l,free.b)
brush = CreatePatternBrush_(ImageID(image))
SetClassLong_(WindowID(window),#GCL_HBRBACKGROUND,brush)
InvalidateRect_(WindowID(window),0,#True)
If free
FreeImage(image)
EndIf
ProcedureReturn brush
EndProcedure
Procedure Checkerize(xBase.l,yBase.l,w.l,h.l,level.l)
colour = RGB(level,level,level)
xStart = xBase-1
switch.b = 0
For y = yBase-1 To yBase+h
For x = xStart To w Step 2
If x>-1 And y>-1
Plot(x,y,colour)
EndIf
Next
If switch
switch = 0
xStart = xBase-1
Else
switch = 1
xStart = xBase
EndIf
Next
ProcedureReturn
EndProcedure
Procedure.f Beat(a.f,b.f)
If a>b
ProcedureReturn a
Else
ProcedureReturn b
EndIf
EndProcedure
Procedure.f Defeat(a.f,b.f)
If a<b
ProcedureReturn a
Else
ProcedureReturn b
EndIf
EndProcedure
Procedure SetGadgetBrush(gad,image.l,free.b)
;If OpenLibrary(10, "uxtheme.dll")
; If CallFunction(10, "SetWindowTheme", GadgetID(gad), @null.w, @null.w) = 0
; ;Debug "Theme turned off for ScrollAreaGadget"
; EndIf
; CloseLibrary(10)
;EndIf
ScrollAreaChild = GetWindow_(GadgetID(gad),#GW_CHILD)
SetWindowLong_(GadgetID(gad),#GWL_STYLE,GetWindowLong_(GadgetID(gad),#GWL_STYLE) | #WS_CLIPCHILDREN)
brush = CreatePatternBrush_(ImageID(image))
SetClassLong_(ScrollAreaChild, #GCL_HBRBACKGROUND,brush)
InvalidateRect_(ScrollAreaChild, #Null, #True)
If free
FreeImage(image)
EndIf
EndProcedure
Procedure.l RedBox(w.l,h.l)
img = CreateImage(#PB_Any,w,h)
StartDrawing(ImageOutput(img))
Box(0,0,w,h,#Red)
StopDrawing()
ProcedureReturn img
EndProcedure
Procedure.b Tile(img.l,tile.l,alpha.b)
If Not IsImage(img)
ProcedureReturn #False
EndIf
If Not IsImage(tile)
ProcedureReturn #False
EndIf
iw = ImageWidth(img)
ih = ImageHeight(img)
tw = ImageWidth(tile)
th = ImageHeight(tile)
Repeat
Repeat
If alpha
DrawAlphaImage(ImageID(tile),x,y)
Else
DrawImage(ImageID(tile),x,y)
EndIf
x+tw
Until x>iw
y+th
x=0
Until y>ih
ProcedureReturn #True
EndProcedure
;- SCROLLING FUNCTIONS
;{ STRUCTURE
Structure ODSAG
sag.l ; gadgetnumber for actual ScrollAreaGadget
container.l ; gadgetnumber (probably redundant)
window.l
x.f
y.f
w.f
h.f
sx.f ; innerscrollX
sy.f ; innerscrollY
sw.f ; innerscrollwidth
sh.f ; innerscrollheight
steps.w
barT.w
; button nums
xSlider.l
lbutton.l
rbutton.l
ySlider.l
ubutton.l
dbutton.l
xBar.l
yBar.l
xBarI.l
yBarI.l
; image nums
xSliderI.l
ySliderI.l
tile.l
EndStructure
;}
Procedure.l GadXFromScrollX(*SAG.ODSAG)
x.f = GadgetWidth(*SAG\xBar) / *SAG\sw * *SAG\sx
x = Beat(x,0)
x+GadgetX(*SAG\xBar)
xgmax = GadgetX(*SAG\rButton)-GadgetWidth(*SAG\xSlider)
ProcedureReturn Defeat(x,xgmax)
EndProcedure
Procedure.l GadYFromScrollY(*SAG.ODSAG)
y.f = GadgetHeight(*SAG\yBar) / *SAG\sh * *SAG\sy
y = Beat(y,0)
y+GadgetY(*SAG\yBar)
ygmax = GadgetY(*SAG\dButton)-GadgetHeight(*SAG\ySlider)
ProcedureReturn Defeat(y,ygmax)
EndProcedure
Procedure.l ScrollXFromGadX(*SAG.ODSAG,gx.f)
sx.f = *SAG\sw / GadgetWidth(*SAG\xBar) * gx
sx = Beat(sx,0)
ProcedureReturn sx;Defeat(sx,*SAG\sw - *SAG\w)
EndProcedure
Procedure.l ScrollYFromGadY(*SAG.ODSAG,gy.f)
sy.f = *SAG\sh / GadgetHeight(*SAG\yBar) * gy
sy = Beat(sy,0)
ProcedureReturn sy;Defeat(sy,*SAG\sh - *SAG\h)
EndProcedure
Procedure UpdateODSAG(*SAG.ODSAG)
*SAG\sx = Beat(0,*SAG\sx)
*SAG\sy = Beat(0,*SAG\sy)
SetGadgetAttribute(*SAG\sag,#PB_ScrollArea_X,*SAG\sx)
barw.f = *SAG\w - GadgetWidth(*SAG\lButton) - GadgetWidth(*SAG\rButton) - *SAG\barT
If barw<>GadgetWidth(*SAG\xBar) Or Not IsImage(*SAG\xBarI)
If IsImage(*SAG\xBarI)
FreeImage(*SAG\xBarI)
EndIf
img = CreateImage(#PB_Any,barw,*SAG\barT)
StartDrawing(ImageOutput(img))
Box(0,0,barw,*SAG\barT,RGB(128,0,255))
StopDrawing()
SetGadgetState(*SAG\xBar,ImageID(img))
SetWindowPos_(GadgetID(*SAG\xBar),#HWND_BOTTOM,0,0,0,0,#SWP_NOSIZE|#SWP_NOMOVE)
*SAG\xBarI = img
EndIf
w.f = barw * *SAG\w / *SAG\sw
If w<>GadgetWidth(*SAG\xSlider)
If IsImage(*SAG\xSliderI)
FreeImage(*SAG\xSliderI)
EndIf
img = CreateImage(#PB_Any,w,*SAG\barT)
StartDrawing(ImageOutput(img))
Box(0,0,w,*SAG\barT,RGB(0,58,89))
Checkerize(0,0,w,*SAG\barT,0)
StopDrawing()
SetGadgetState(*SAG\xSlider,ImageID(img))
*SAG\xSliderI = img
EndIf
ResizeGadget(*SAG\xSlider,GadXFromScrollX(*SAG),#PB_Ignore,#PB_Ignore,#PB_Ignore)
SetGadgetAttribute(*SAG\sag,#PB_ScrollArea_Y,*SAG\sy)
barh.f = *SAG\h - GadgetHeight(*SAG\uButton) - GadgetHeight(*SAG\dButton) - *SAG\barT
h.f = barh * *SAG\h / *SAG\sh
If barh<>GadgetHeight(*SAG\yBar) Or Not IsImage(*SAG\yBarI)
If IsImage(*SAG\yBarI)
FreeImage(*SAG\yBarI)
EndIf
img = CreateImage(#PB_Any,*SAG\barT,barh)
StartDrawing(ImageOutput(img))
Box(0,0,*SAG\barT,barh,RGB(128,0,255))
StopDrawing()
SetGadgetState(*SAG\yBar,ImageID(img))
SetWindowPos_(GadgetID(*SAG\yBar),#HWND_BOTTOM,0,0,0,0,#SWP_NOSIZE|#SWP_NOMOVE)
*SAG\yBarI = img
EndIf
If h<>GadgetHeight(*SAG\ySlider)
If IsImage(*SAG\ySliderI)
FreeImage(*SAG\ySliderI)
EndIf
img = CreateImage(#PB_Any,*SAG\barT,h)
StartDrawing(ImageOutput(img))
Box(0,0,*SAG\barT,h,RGB(0,58,89))
Checkerize(0,0,*SAG\barT,h,0)
StopDrawing()
SetGadgetState(*SAG\ySlider,ImageID(img))
*SAG\ySliderI = img
EndIf
gady=GadYFromScrollY(*SAG)
ResizeGadget(*SAG\ySlider,#PB_Ignore,gady,#PB_Ignore,#PB_Ignore)
EndProcedure
Procedure CheckODSAG(*SAG.ODSAG,we,eg)
If we<>#PB_Event_Gadget
ProcedureReturn
EndIf
*SAG\sx = GetGadgetAttribute(*SAG\sag,#PB_ScrollArea_X)
*SAG\sy = GetGadgetAttribute(*SAG\sag,#PB_ScrollArea_Y)
xmax = *SAG\sw - *SAG\w + *SAG\barT
ymax = *SAG\sh - *SAG\h + *SAG\barT
Select eg
;Case *SAG\sag
;MessageRequester("report","background was pressed!",0)
Case *SAG\lButton
Repeat
*SAG\sx-*SAG\steps
If *SAG\sx<0
*SAG\sx = 0
update=#True
Break
EndIf
UpdateODSAG(*SAG)
Delay(50)
Until Not GetAsyncKeyState_(#VK_LBUTTON)
Case *SAG\rButton
Repeat
*SAG\sx+*SAG\steps
If *SAG\sx>xmax
*SAG\sx = xmax
update=#True
Break
EndIf
UpdateODSAG(*SAG)
Delay(50)
Until Not GetAsyncKeyState_(#VK_LBUTTON)
Case *SAG\uButton
Repeat
*SAG\sy-*SAG\steps
If *SAG\sy<0
*SAG\sy = 0
update=#True
Break
EndIf
UpdateODSAG(*SAG)
Delay(50)
Until Not GetAsyncKeyState_(#VK_LBUTTON)
Case *SAG\dButton
Repeat
*SAG\sy+*SAG\steps
If *SAG\sy>ymax
*SAG\sy = ymax
update=#True
Break
EndIf
UpdateODSAG(*SAG)
Delay(50)
Until Not GetAsyncKeyState_(#VK_LBUTTON)
Case *SAG\xSlider
MonitorXSlider:
offset = WindowMouseX(*SAG\window)-GadgetX(*SAG\xSlider)+(GadgetWidth(*SAG\xSlider)/2)
Repeat
Repeat
we=WindowEvent()
If we=#WM_LBUTTONUP
update=#True
Break 2
EndIf
Until we=#WM_MOUSEMOVE
*SAG\sx = ScrollXFromGadX(*SAG,WindowMouseX(*SAG\window)-offset)
UpdateODSAG(*SAG)
Until Not GetAsyncKeyState_(#VK_LBUTTON)
Case *SAG\ySlider
MonitorYSlider:
offset = WindowMouseY(*SAG\window)-GadgetY(*SAG\ySlider)+(GadgetHeight(*SAG\ySlider)/2)
Repeat
Repeat
we=WindowEvent()
If we=#WM_LBUTTONUP
update=#True
Break 2
EndIf
Until we=#WM_MOUSEMOVE
*SAG\sy = ScrollYFromGadY(*SAG,WindowMouseY(*SAG\window)-offset)
UpdateODSAG(*SAG)
Until Not GetAsyncKeyState_(#VK_LBUTTON)
Case *SAG\xBar
; jump left or right
mx.f = WindowMouseX(*SAG\window)-GadgetX(*SAG\xBar)
mx-(GadgetWidth(*SAG\xSlider)/2)
mx = beat(1,mx)
*SAG\sx = ScrollXFromGadX(*SAG,mx)
UpdateODSAG(*SAG)
If GetAsyncKeyState_(#VK_LBUTTON)
Goto MonitorXSlider
EndIf
Case *SAG\yBar
; jump up or down
my.f = WindowMouseY(*SAG\window)-GadgetY(*SAG\yBar)
my-(GadgetHeight(*SAG\ySlider)/2)
my = beat(1,my)
*SAG\sy = ScrollYFromGadY(*SAG,my)
UpdateODSAG(*SAG)
If GetAsyncKeyState_(#VK_LBUTTON)
Goto MonitorYslider
EndIf
EndSelect
If update
UpdateODSAG(*SAG)
EndIf
EndProcedure
Procedure ResizeODSAG(*SAG.ODSAG,w.l,h.l)
If w<>#PB_Ignore
*SAG\w = w
EndIf
If h<>#PB_Ignore
*SAG\h = h
EndIf
nativewidth = GetSystemMetrics_(#SM_CYHSCROLL)
major = Beat(*SAG\barT,nativewidth)
minor = Defeat(*SAG\barT,nativewidth)
If *SAG\barT<nativewidth
sagw=*SAG\w+(nativewidth-*SAG\barT)
sagh=*SAG\h+(nativewidth-*SAG\barT)
cw=*SAG\w-*SAG\barT
ch=*SAG\h-*SAG\barT
Else
sagw=*SAG\w-major+minor
sagh=*SAG\h-major+minor
cw=sagw-minor
ch=sagh-minor
EndIf
ResizeGadget(*SAG\container,*SAG\x,*SAG\y,cw,ch)
ResizeGadget(*SAG\sag,0,0,sagw,sagh)
SetGadgetAttribute(*SAG\sag,#PB_ScrollArea_InnerWidth,*SAG\sw)
SetGadgetAttribute(*SAG\sag,#PB_ScrollArea_InnerHeight,*SAG\sh)
tiled = CreateImage(#PB_Any,*SAG\sw,*SAG\sh)
StartDrawing(ImageOutput(tiled))
Tile(tiled,*SAG\tile,#False)
StopDrawing()
SetGadgetBrush(*SAG\sag,*SAG\tile,#True)
ResizeGadget(*SAG\lButton,*SAG\x,*SAG\y+*SAG\h-*SAG\barT,*SAG\barT,*SAG\barT)
ResizeGadget(*SAG\rButton,*SAG\x+*SAG\w-*SAG\barT-*SAG\barT,*SAG\y+*SAG\h-*SAG\barT,*SAG\barT,*SAG\barT)
ResizeGadget(*SAG\uButton,*SAG\x+*SAG\w-*SAG\barT,*SAG\y,*SAG\barT,*SAG\barT)
ResizeGadget(*SAG\dButton,*SAG\x+*SAG\w-*SAG\barT,*SAG\y+*SAG\h-*SAG\barT-*SAG\barT,*SAG\barT,*SAG\barT)
ResizeGadget(*SAG\xBar,GadgetX(*SAG\lButton)+GadgetWidth(*SAG\lButton),*SAG\y+*SAG\h-*SAG\barT,*SAG\w-GadgetWidth(*SAG\lButton)-GadgetWidth(*SAG\rButton)-*SAG\barT,*SAG\barT)
ResizeGadget(*SAG\yBar,*SAG\x+*SAG\w-*SAG\barT,GadgetY(*SAG\uButton)+GadgetHeight(*SAG\uButton),*SAG\barT,*SAG\y-GadgetHeight(*SAG\uButton)-GadgetHeight(*SAG\dButton))
ResizeGadget(*SAG\xSlider,*SAG\x,*SAG\y+*SAG\h-*SAG\barT,100,*SAG\barT)
ResizeGadget(*SAG\ySlider,*SAG\x+*SAG\w-*SAG\barT,*SAG\y+*SAG\h-*SAG\barT,*SAG\barT,100)
UpdateODSAG(*SAG)
EndProcedure
Procedure ODSAG(*SAG.ODSAG,x,y,w,h,innerwidth.l,innerheight.l,steps.l,scrollbarwidth.w,tile.l,window.l)
*SAG\x = x
*SAG\y = y
*SAG\w = w
*SAG\h = h
*SAG\sw = innerwidth
*SAG\sh = innerheight
*SAG\steps = steps
*SAG\barT = scrollbarwidth
*SAG\tile = tile
*SAG\window = window
*SAG\container = ContainerGadget(#PB_Any,0,0,1,1,#PB_Container_BorderLess)
*SAG\sag = ScrollAreaGadget(#PB_Any,0,0,1,1,*SAG\sw,*SAG\sh,*SAG\steps,#PB_ScrollArea_BorderLess)
exitbutton = ButtonGadget(#PB_Any,100,100,200,150,"Close")
CloseGadgetList()
CloseGadgetList()
; create LEFT arrow button
;scrollLimg = LoadImage(#PB_Any,ImageFol+"ScrollL.png")
scrollLimg = RedBox(*SAG\barT,*SAG\barT)
*SAG\lButton = ImageGadget(#PB_Any,*SAG\x,*SAG\y+*SAG\h-*SAG\barT,*SAG\barT,*SAG\barT,ImageID(scrollLimg))
; create RIGHT arrow button
;scrollRimg = LoadImage(#PB_Any,ImageFol+"ScrollR.png")
scrollRimg = RedBox(*SAG\barT,*SAG\barT)
*SAG\rButton = ImageGadget(#PB_Any,*SAG\x+*SAG\w-*SAG\barT-*SAG\barT,*SAG\y+*SAG\h-*SAG\barT,*SAG\barT,*SAG\barT,ImageID(scrollRimg))
; create UP arrow button
;scrollUimg = LoadImage(#PB_Any,ImageFol+"ScrollU.png")
scrollUimg = RedBox(*SAG\barT,*SAG\barT)
*SAG\uButton = ImageGadget(#PB_Any,*SAG\x+*SAG\w-*SAG\barT,*SAG\y,*SAG\barT,*SAG\barT,ImageID(scrollUimg))
; create DOWN arrow button
; scrollDimg = LoadImage(#PB_Any,ImageFol+"ScrollD.png")
scrollDimg = RedBox(*SAG\barT,*SAG\barT)
*SAG\dButton = ImageGadget(#PB_Any,*SAG\x+*SAG\w-*SAG\barT,*SAG\y+*SAG\h-*SAG\barT-*SAG\barT,*SAG\barT,*SAG\barT,ImageID(scrollDimg))
*SAG\xBar = ImageGadget(#PB_Any,GadgetX(*SAG\lButton)+GadgetWidth(*SAG\lButton),*SAG\y+*SAG\h-*SAG\barT,*SAG\w-GadgetWidth(*SAG\lButton)-GadgetWidth(*SAG\rButton)-*SAG\barT,*SAG\barT,0)
; allow bg imagegadget to be z-ordered in "UpdateODSAG" procedure
SetWindowLong_(GadgetID(*SAG\xBar),#GWL_STYLE,GetWindowLong_(GadgetID(*SAG\xBar),#GWL_STYLE)|#WS_CLIPSIBLINGS)
*SAG\yBar = ImageGadget(#PB_Any,*SAG\x+*SAG\w-*SAG\barT,GadgetY(*SAG\uButton)+GadgetHeight(*SAG\uButton),*SAG\barT,*SAG\y-GadgetHeight(*SAG\uButton)-GadgetHeight(*SAG\dButton),0)
; allow bg imagegadget to be z-ordered in "UpdateODSAG" procedure
SetWindowLong_(GadgetID(*SAG\yBar),#GWL_STYLE,GetWindowLong_(GadgetID(*SAG\yBar),#GWL_STYLE)|#WS_CLIPSIBLINGS)
; X slider
*SAG\xSlider = ImageGadget(#PB_Any,*SAG\x,*SAG\y+*SAG\h-*SAG\barT,100,*SAG\barT,0)
; Y slider
*SAG\ySlider = ImageGadget(#PB_Any,*SAG\x+*SAG\w-*SAG\barT,*SAG\y+*SAG\h-*SAG\barT,*SAG\barT,100,0)
ResizeODSAG(*SAG,*SAG\w,*SAG\h)
EndProcedure
ExamineDesktops()
ww.f = DesktopWidth(0)-50
wh.f = DesktopHeight(0)-100
win = OpenWindow(#PB_Any,0,0,ww,wh,"test",#PB_Window_ScreenCentered)
If CreateGadgetList(WindowID(win))
brush = CreateImage(#PB_Any,ww,wh)
StartDrawing(ImageOutput(brush))
Box(0,0,ww,wh,#Gray)
x=Random(100)
y=Random(100)
w=ww-x-Random(100)
h=wh-y-Random(100)
DrawBox(x-1,y-1,w+1,h+1,#Green)
StopDrawing()
SetWindowBrush(win,brush,#True)
tile = LoadImage(#PB_Any,"C:\inner-pattern.bmp") ; this is the background tile for the inner area.
ODSAG(@SAG.ODSAG,x,y,w,h,ww+5000,wh+2000,10,Random(50)+8,tile,win)
EndIf
Repeat
we = WindowEvent()
eg = EventGadget()
If we=#PB_Event_Gadget
If eg=exitbutton
End
EndIf
EndIf
CheckODSAG(@SAG,we,eg)
Delay(10)
Until GetAsyncKeyState_(#VK_ESCAPE)
End