[MAXGUIEX] Panel with scrollbar?

BlitzMax Forums/BlitzMax GUI Programming/[MAXGUIEX] Panel with scrollbar?

Hello guys!

I need to put a lot of different gadgets in a relative small main window. Is there an easy and bug free way to make a "scrollable panel"? - The window itself can't be resizable. :(

Grisu

Hi Grisu,

I started hacking one into MaxGUI.ProxyGadgets (back in the days when I had some free time :P), but it isn't quite finished yet.

Still, if you want to have a play, copy and paste this into a new file and import it into your project.

WARNING: ALPHA CODE (NOT TESTED, FEATURE COMPLETE OR BUG FREE)

Strict

Import MaxGUI.MaxGUI

Rem
bbdoc: Creates a panel that can automatically show/hide scrollbars so that gadgets outside the initial visible region can be reached.
about: There are two main parts of a scroll-panel.

The TScrollPanel instance itself represents the entire scroll-panel, which can be manipulated (e.g. resized/shown/hidden) using the 
standard MaxGUI commands.

Perhaps more importantly, the client-area is the panel to which all your child gadgets should be added. Then client-area panel instance
can be retrieved using the #ScrollPanelClient command. Other than to add gadgets, this client-area panel should not be used.

Everytime a gadget is added, moved or freed from the client-area, #UpdateScrollPanel must be called to update the scroll-bars, although
the scroll-bars should be updated automatically upon window resize.

See Also: #ScrollPanelClient and #UpdateScrollPanel.
End Rem
Function CreateScrollPanel:TScrollPanel( x,y,w,h,group:TGadget,flags=0 )
	
	Return New TScrollPanel.Create(x,y,w,h,group,flags)
	
EndFunction

Rem
bbdoc: Retrieves the client-area panel instance which is used to add gadgets to the scrollpanel.
about: Remember to call #UpdateScrollPanel after adding/moving/freeing child gadgets. The client-area
panel instance should only be used for adding gadgets; the TScrollPanel instance itself allows for
manipulation of the entire TScrollPanel instead.

See #CreateScrollPanel for more information.
End Rem
Function ScrollPanelClient:TGadget( scrollpanel:TScrollPanel )
	Return scrollpanel.pnlClientArea
EndFunction

Rem
bbdoc: Recalculates and updates the scrollbars according to the current layout of the child gadgets.
about: See #CreateScrollPanel for more information.
End Rem
Function UpdateScrollPanel( scrollpanel:TScrollPanel )
	scrollpanel.Update()
EndFunction

Type TScrollPanel Extends TProxyGadget
	
	Field pnlEntire:TGadget
	Field pnlViewport:TGadget
	Field pnlClientArea:TGadget
	Field scrHorizontal:TGadget
	Field scrVertical:TGadget
	
	Field oldH%, oldV%, clientW%, clientH%
	
	Const SCROLL_WIDTH% = 18
	
	Method Create:TScrollPanel(pX%, pY%, pWidth%, pHeight%, pParent:TGadget, pFlags% = 0)
	
		pnlEntire = CreatePanel(pX,pY,pWidth,pHeight,pParent,pFlags);HideGadget(pnlEntire);SetProxy(pnlEntire)
		pnlViewport = CreatePanel(0,0,ClientWidth(),ClientHeight(), pnlEntire)
		pnlClientArea = CreatePanel(0,0,ClientWidth(),ClientHeight(), pnlViewport)
		scrHorizontal = CreateSlider(0,ClientHeight()-SCROLL_WIDTH,ClientWidth()-SCROLL_WIDTH,SCROLL_WIDTH, pnlEntire, SLIDER_HORIZONTAL|SLIDER_SCROLLBAR )
		scrVertical = CreateSlider(ClientWidth()-SCROLL_WIDTH,0,SCROLL_WIDTH,ClientHeight()-SCROLL_WIDTH, pnlEntire, SLIDER_VERTICAL|SLIDER_SCROLLBAR )
		
		SetGadgetLayout(pnlViewport, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED )
		SetGadgetLayout(pnlClientArea, EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED, EDGE_CENTERED )
		SetGadgetLayout(scrHorizontal, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED )
		SetGadgetLayout(scrVertical, EDGE_CENTERED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED )
		
		HideGadget(scrHorizontal);HideGadget(scrVertical)
		
		clientW = pnlViewport.ClientWidth();clientH = pnlViewport.ClientHeight()
		
		ShowGadget(pnlEntire)
		
		Return Self
	
	EndMethod
	
	Method New()
		
		AddHook EmitEventHook,eventHandler,Self, 1
		RemoveVerticalScroll();RemoveHorizontalScroll()
		
	EndMethod
	
	Method CleanUp()
	
		RemoveHook EmitEventHook, eventHandler, Self
		If pnlEntire Then HideGadget(pnlEntire);FreeGadget(pnlEntire)
	
	EndMethod
	
	Method eventHook:Object(pID%, pData:Object, pContext:Object)
	
		Local tmpEvent:TEvent = TEvent(pData)
		If tmpEvent = Null Then Return pData
		
		Select tmpEvent.id
			
			Case EVENT_WINDOWSIZE
			
				If CheckParent(pnlEntire, TGadget(tmpEvent.source)) Then Update() 
			
			Case EVENT_GADGETACTION
			
				Select tmpEvent.source
				
					Case scrHorizontal;ShowRegion(SliderValue(scrHorizontal),oldV);pData = Null
					Case scrVertical;ShowRegion(oldH,SliderValue(scrVertical));pData = Null
				
				EndSelect
			
		EndSelect
		
		Return pData
		
	EndMethod
	
	Method ShowRegion(pHSlider%, pVSlider%)
	
		Local tmpRelativeX% = oldH-pHSlider, tmpRelativeY% = oldV-pVSlider
		oldH = pHSlider;oldV = pVSlider
		
		SetGadgetShape(pnlClientArea,GadgetX(pnlClientArea)+tmpRelativeX,GadgetY(pnlClientArea)+tmpRelativeY,pnlViewport.ClientWidth()-(GadgetX(pnlClientArea)+tmpRelativeX),pnlViewport.ClientHeight()-(GadgetY(pnlClientArea)+tmpRelativeY))
	
	EndMethod
	
	Method SetShape(x,y,w,h)
		Super.SetShape(x,y,w,h)
		Update()
	EndMethod
	
	Method Update()
		
		If Not pnlClientArea Then Return
		
		Local tmpRelW% = (GadgetX(pnlClientArea)+GadgetWidth(pnlClientArea)-clientW)
		Local tmpRelH% = (GadgetY(pnlClientArea)+GadgetHeight(pnlClientArea)-clientH)
		
		If tmpRelW > 0 Or tmpRelW < SliderValue(scrHorizontal) Then tmpRelW = 0
		If tmpRelH > 0 Or tmpRelH < SliderValue(scrVertical) Then tmpRelH = 0
		
		ShowRegion( SliderValue(scrHorizontal)+tmpRelW, SliderValue(scrVertical)+tmpRelH )
		clientW = pnlViewport.ClientWidth();clientH = pnlViewport.ClientHeight()
		
		Local tmpLeft% = GadgetX(pnlClientArea), tmpRight%, tmpTop%=GadgetY(pnlClientArea), tmpBottom%
		
		For Local tmpChild:TGadget = EachIn pnlClientArea.kids
		
			'tmpLeft = Min(tmpLeft,GadgetX(tmpChild)+GadgetX(pnlClientArea))
			tmpRight = Max(tmpRight,GadgetX(tmpChild)+GadgetWidth(tmpChild))
			'tmpTop = Min(tmpTop,GadgetY(tmpChild)+GadgetY(pnlClientArea))
			tmpBottom = Max(tmpBottom,GadgetY(tmpChild)+GadgetHeight(tmpChild))
		
		Next
		
		If tmpLeft < 0 Or tmpRight > pnlClientArea.ClientWidth() Then
		
			AddHorizontalScroll(pnlClientArea.ClientWidth(),tmpLeft,tmpRight)
			
		Else
		
			RemoveHorizontalScroll()
			
		EndIf
		
		If tmpTop < 0 Or tmpBottom > pnlClientArea.ClientHeight() Then
		
			AddVerticalScroll(pnlClientArea.ClientHeight(),tmpTop,tmpBottom)
			
		Else
		
			RemoveVerticalScroll()
			
		EndIf
	
	EndMethod
	
	Method AddVerticalScroll(pVisible%, pTop%, pBottom%)
		
		If scrVertical Then
			
			SetGadgetShape(pnlViewport, 0, 0, ClientWidth() - SCROLL_WIDTH, GadgetHeight(pnlViewport))
			SetGadgetShape(pnlClientArea, GadgetX(pnlClientArea), GadgetY(pnlClientArea), pnlViewport.ClientWidth() - GadgetX(pnlClientArea), pnlViewport.ClientHeight() - GadgetY(pnlClientArea)) 
			
			SetGadgetShape(scrVertical, ClientWidth()-SCROLL_WIDTH, 0, SCROLL_WIDTH, GadgetHeight(pnlViewport))
			SetSliderRange(scrVertical, pVisible, Max(pBottom-Min(0,pTop),pVisible));SetSliderValue(scrVertical,Max(-pTop,0));ShowGadget(scrVertical)
		
		EndIf
		
	EndMethod
	
	Method RemoveVerticalScroll()
	
		If scrVertical Then
			
			SetGadgetShape(pnlViewport, 0, 0, ClientWidth(), GadgetHeight(pnlViewport))
			SetGadgetShape(pnlClientArea, GadgetX(pnlClientArea), GadgetY(pnlClientArea), pnlViewport.ClientWidth() - GadgetX(pnlClientArea), pnlViewport.ClientHeight() - GadgetY(pnlClientArea)) 
			HideGadget(scrVertical)
			
		EndIf
	
	EndMethod
	
	Method AddHorizontalScroll(pVisible%, pLeft%, pRight%)
	
		If scrHorizontal Then
			
			SetGadgetShape(pnlViewport, 0, 0, GadgetWidth(pnlViewport), ClientHeight()-SCROLL_WIDTH )
			SetGadgetShape(pnlClientArea, GadgetX(pnlClientArea), GadgetY(pnlClientArea), pnlViewport.ClientWidth() - GadgetX(pnlClientArea), pnlViewport.ClientHeight() - GadgetY(pnlClientArea)) 
			
			SetGadgetShape(scrHorizontal, 0, ClientHeight()-SCROLL_WIDTH, GadgetWidth(pnlViewport), SCROLL_WIDTH)
			SetSliderRange(scrHorizontal, pVisible, Max(pRight-Min(0,pLeft),pVisible));SetSliderValue(scrHorizontal,Max(-pLeft,0));ShowGadget(scrHorizontal)
		
		EndIf
		
	EndMethod
	
	Method RemoveHorizontalScroll()
	
		If scrHorizontal Then
			
			SetGadgetShape(pnlViewport, 0, 0, GadgetWidth(pnlViewport), ClientHeight() )
			SetGadgetShape(pnlClientArea, GadgetX(pnlClientArea), GadgetY(pnlClientArea), pnlViewport.ClientWidth() - GadgetX(pnlClientArea), pnlViewport.ClientHeight() - GadgetY(pnlClientArea)) 
			HideGadget(scrHorizontal)
			
		EndIf
	
	EndMethod
	
	Function eventHandler:Object(pID%, pData:Object, pContext:Object)
	
		Local tmpSuperPanel:TScrollPanel = TScrollPanel(pContext)
		If tmpSuperPanel Then pData = tmpSuperPanel.eventHook(pID%, pData:Object, pContext:Object)
		Return pData 
	
	EndFunction
	
	Function CheckParent%( pGadget:TGadget, pParentToCheck:TGadget )
		
		If pGadget = pParentToCheck Then Return True
		If pGadget.parent Then Return CheckParent(pGadget.parent, pParentToCheck)
		
	EndFunction
	
EndType


Add gadgets to the panel returned by ScrollPanelClient(), and call UpdateScrollPanel() when you are finished to have the scrollbars update.

I have a nagging feeling that there is some strange behavior when resizing windows or it might have been with performance but you are welcome to have a play around and report back.

Have fun! ;-)

As usual you save my day Seb. ;) Thanks!

At least I have something to waste my days off work on...