scrollpanel.bmx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. Strict
  2. Import MaxGUI.MaxGUI
  3. ' TScrollPanel Proxy Gadget
  4. ' Author: Seb Hollington
  5. Const SCROLLPANEL_SUNKEN:Int = 1
  6. Const SCROLLPANEL_HALWAYS:Int = 2
  7. Const SCROLLPANEL_VALWAYS:Int = 4
  8. Const SCROLLPANEL_HSCALING:Int = 8
  9. Const SCROLLPANEL_VSCALING:Int = 16
  10. Const SCROLLPANEL_HNEVER:Int = 32
  11. Const SCROLLPANEL_VNEVER:Int = 64
  12. Rem
  13. bbdoc: Creates a scrollable panel.
  14. about: A scroll panel can be used to present a large number of gadgets in a small area. Scrollbars are displayed to allow the
  15. user to move around a client-area that is viewed through a, typically smaller, viewport. The #ScrollPanelX and #ScrollPanelY functions
  16. can be used to retrieve the current scroll position, and the #ScrollScrollPanel command, to set the scroll position. A @TScrollPanel gadget
  17. emits the following event when %{the user} scrolls around the scroll area:
  18. [ @{Event} | @{EventX} | @{EventY}
  19. * EVENT_GADGETACTION | New value of #ScrollPanelX. | New value of #ScrollPanelY.
  20. ]
  21. Any combination of the following style flags are supported:
  22. [ @Constant | @Meaning
  23. * SCROLLPANEL_SUNKEN | The scroll-panel will be drawn with a sunken border.
  24. * SCROLLPANEL_HALWAYS | The horizontal scroll-bar will be shown at all times (even if not necessary).
  25. * SCROLLPANEL_VALWAYS | The vertical scroll-bar will be shown at all times (even if not necessary).
  26. * SCROLLPANEL_HNEVER | The horizontal scroll-bar will never be shown (even if client-area width is greater than viewport's).
  27. * SCROLLPANEL_VNEVER | The vertical scroll-bar will never be shown (even if client-area height is greater than viewport's).
  28. ]
  29. The above can also be combined with any of the following behavioural flags which determine how the scrollable client-area resizes with the viewport:
  30. [ @Constant | @Meaning
  31. * SCROLLPANEL_HSCALING | The client area's width grows uniformly as the viewport is sized.
  32. * SCROLLPANEL_VSCALING | The client area's height grows uniformly as the viewport is sized.
  33. ]
  34. [
  35. * The @TScrollPanel instance itself represents the viewport of the scroll-panel, which can be manipulated (e.g. resized/shown/hidden) using the
  36. standard MaxGUI commands.
  37. * The client area is the panel that will actually be scrolled and is retrieved using the #ScrollPanelClient command. This is the panel
  38. whose dimensions determine the total scrollable area, and is also the panel that all your child gadgets should be added to.
  39. ]
  40. <img src="scroll_dimensions.png" />
  41. The dimensions given above can each be retrieved programatically:
  42. {{
  43. GadgetWidth( myScrollPanel ) 'Gadget Width
  44. GadgetHeight( myScrollPanel ) 'Gadget Height
  45. ClientWidth( myScrollPanel ) 'Viewport Width
  46. ClientHeight( myScrollPanel ) 'Viewport Height
  47. ClientWidth( ScrollPanelClient( myScrollPanel ) ) 'Client Area Width
  48. ClientHeight( ScrollPanelClient( myScrollPanel ) ) 'Client Area Height
  49. }}
  50. And the gadget and client dimensions can be set programatically using (viewport sizing is handled automatically):
  51. {{
  52. 'Set Gadget dimensions (and position).
  53. SetGadgetShape( myScrollPanel, x, y, w, h )
  54. 'Set Client Area dimensions (position parameters are ignored).
  55. SetGadgetShape( ScrollPanelClient( myScrollPanel ), 0, 0, w, h )
  56. }}
  57. See Also: #ScrollPanelClient, #FitScrollPanelClient, #ScrollScrollPanel, #ScrollPanelX, #ScrollPanelY and #FitScrollPanelClient.
  58. End Rem
  59. Function CreateScrollPanel:TScrollPanel( x,y,w,h,group:TGadget,flags=0 )
  60. Return New TScrollPanel.Create(x,y,w,h,group,flags)
  61. EndFunction
  62. Rem
  63. bbdoc: Retrieves the panel that is scrolled.
  64. about: This panel represents the total scrollable region of the gadget. As such, use #SetGadgetShape on this panel to alter the
  65. scrollable region (the xpos and ypos parameters will be ignored) or use the helper function #FitScrollPanelClient to resize the client area to
  66. common dimensions. In either case, it is important to note that, contrary to typical MaxGUI behaviour, resizing the client panel
  67. %{will not alter the position or dimensions of the children}, irrespective of any sizing behaviour previously defined using #SetGadgetLayout.
  68. See #CreateScrollPanel for more information.
  69. End Rem
  70. Function ScrollPanelClient:TGadget( scrollpanel:TScrollPanel )
  71. Return scrollpanel.pnlClientArea
  72. EndFunction
  73. Const SCROLLPANEL_SIZETOKIDS:Int = 0
  74. Const SCROLLPANEL_SIZETOVIEWPORT:Int = 1
  75. Rem
  76. bbdoc: Helper function that resizes the client area to common dimensions.
  77. about: This function resizes the scrollable area of a @TScrollPanel widget. Any child gadgets will retain their current
  78. position and dimensions, irrespective of any sizing behaviour previously defined using #SetGadgetLayout. This function will
  79. also reset the current visible area, to the furthest top-left.
  80. [
  81. * @scrollpanel: The scrollpanel whose client you want to resize.
  82. * @fitType: Should be one of the following constants:
  83. ]
  84. [ @Constant | @Meaning
  85. * SCROLLPANEL_SIZETOKIDS | The client area will be resized so that its width and height are just enough to enclose all child gadgets.
  86. * SCROLLPANEL_SIZETOVIEWPORT | The client area will be resized so that it is the same size that the viewport is currently (effectively removing the scrollbars).
  87. ]
  88. See #CreateScrollPanel and #ScrollPanelClient for more information.
  89. End Rem
  90. Function FitScrollPanelClient( scrollpanel:TScrollPanel, fitType% = SCROLLPANEL_SIZETOKIDS )
  91. Select fitType
  92. Case SCROLLPANEL_SIZETOKIDS
  93. scrollpanel.FitToChildren()
  94. Case SCROLLPANEL_SIZETOVIEWPORT
  95. scrollpanel.FitToViewport()
  96. EndSelect
  97. EndFunction
  98. Const SCROLLPANEL_HOLD:Int = -1
  99. Const SCROLLPANEL_TOP:Int = 0
  100. Const SCROLLPANEL_LEFT:Int = 0
  101. Const SCROLLPANEL_BOTTOM:Int = 2147483647
  102. Const SCROLLPANEL_RIGHT:Int = 2147483647
  103. Rem
  104. bbdoc: Scrolls the current viewport to a new position.
  105. about: This function moves the client area of the scroll panel so that the the top-left corner of the viewport is as close
  106. as possible to the specified @{pX}, @{pY} position in the client-area.
  107. <img src="scroll_position.png" />
  108. There are 4 position constants provided:
  109. [ @Constant | @{Position}
  110. * SCROLLPANEL_TOP | Top-most edge.
  111. * SCROLLPANEL_LEFT | Left-most edge.
  112. * SCROLLPANEL_BOTTOM | Bottom-most edge.
  113. * SCROLLPANEL_RIGHT | Right-most edge.
  114. * SCROLLPANEL_HOLD | Current position.
  115. ]
  116. For example, both of these commands...
  117. {{
  118. ScrollScrollPanel( myScrollPanel, SCROLLPANEL_LEFT, SCROLLPANEL_TOP )
  119. ScrollScrollPanel( myScrollPanel, 0, 0 )
  120. }}
  121. ...would scroll to the top-leftmost section of the client area. Conversely, we can scroll to the bottom-right most
  122. region of the client area by calling:
  123. {{
  124. ScrollScrollPanel( myScrollPanel, SCROLLPANEL_RIGHT, SCROLLPANEL_BOTTOM )
  125. }}
  126. If we only want to change just the horizontal or just the vertical scroll position, we can use the SCROLLPANEL_HOLD constant. E.g.
  127. to scroll to the left most side without changing the current vertical scroll position, we could use:
  128. {{
  129. ScrollScrollPanel( myScrollPanel, SCROLLPANEL_LEFT, SCROLLPANEL_HOLD )
  130. }}
  131. See #CreateScrollPanel, #ScrollPanelX, #ScrollPanelY and #ScrollPanelClient for more information.
  132. EndRem
  133. Function ScrollScrollPanel( scrollpanel:TScrollPanel, pX = SCROLLPANEL_TOP, pY = SCROLLPANEL_LEFT )
  134. scrollpanel.ScrollTo( pX, pY )
  135. scrollpanel.Update()
  136. EndFunction
  137. Rem
  138. bbdoc: Returns the x position of the client-area that is currently at the top-left of the viewport.
  139. about: Complementary function to #ScrollPanelY and #ScrollScrollPanel. See #ScrollScrollPanel for a visual representation
  140. of this value.
  141. See #CreateScrollPanel for more information.
  142. EndRem
  143. Function ScrollPanelX:Int( scrollpanel:TScrollpanel )
  144. Return scrollpanel.GetXScroll()
  145. EndFunction
  146. Rem
  147. bbdoc: Returns the y position of the client-area that is currently at the top-left of the viewport.
  148. about: Complementary function to #ScrollPanelX and #ScrollScrollPanel. See #ScrollScrollPanel for a visual representation
  149. of this value.
  150. See #CreateScrollPanel for more information.
  151. EndRem
  152. Function ScrollPanelY:Int( scrollpanel:TScrollpanel )
  153. Return scrollpanel.GetYScroll()
  154. EndFunction
  155. Type TScrollPanel Extends TProxyGadget
  156. Field flags:Int
  157. Field pnlEntire:TGadget
  158. Field pnlViewport:TGadget
  159. Field pnlClientArea:TScrollClient
  160. Field scrHorizontal:TGadget
  161. Field scrVertical:TGadget
  162. Field currentH%, currentV%, clientW%, clientH%
  163. Const SCROLL_WIDTH% = 18
  164. Method New()
  165. AddHook EmitEventHook,eventHandler,Self, -1
  166. RemoveVerticalScroll();RemoveHorizontalScroll()
  167. EndMethod
  168. Method Create:TScrollPanel(pX%, pY%, pWidth%, pHeight%, pParent:TGadget, pFlags% = 0)
  169. Local tmpPanelFlags:Int
  170. flags = pFlags
  171. If flags & (SCROLLPANEL_SUNKEN) Then tmpPanelFlags:|PANEL_SUNKEN
  172. pnlEntire = CreatePanel(pX,pY,pWidth,pHeight,pParent,tmpPanelFlags);HideGadget(pnlEntire);SetProxy(pnlEntire)
  173. pnlViewport = CreatePanel(0,0,pnlEntire.ClientWidth(),pnlEntire.ClientHeight(), pnlEntire)
  174. pnlClientArea = New TScrollClient
  175. pnlClientArea.SetProxy(CreatePanel(0,0,pnlViewport.ClientWidth(),pnlViewport.ClientHeight(), pnlViewport))
  176. scrHorizontal = CreateSlider(0,pnlEntire.ClientHeight()-SCROLL_WIDTH,pnlEntire.ClientWidth()-SCROLL_WIDTH,SCROLL_WIDTH, pnlEntire, SLIDER_HORIZONTAL|SLIDER_SCROLLBAR )
  177. scrVertical = CreateSlider(pnlEntire.ClientWidth()-SCROLL_WIDTH,0,SCROLL_WIDTH,pnlEntire.ClientHeight()-SCROLL_WIDTH, pnlEntire, SLIDER_VERTICAL|SLIDER_SCROLLBAR )
  178. SetGadgetLayout(pnlViewport, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED )
  179. SetGadgetLayout(scrHorizontal, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED )
  180. SetGadgetLayout(scrVertical, EDGE_CENTERED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED )
  181. Select (flags & (SCROLLPANEL_HSCALING|SCROLLPANEL_VSCALING))
  182. Case 0
  183. SetGadgetLayout(pnlClientArea.GetProxy(), EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED, EDGE_CENTERED )
  184. Case SCROLLPANEL_HSCALING
  185. SetGadgetLayout(pnlClientArea.GetProxy(), EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_CENTERED )
  186. Case SCROLLPANEL_VSCALING
  187. SetGadgetLayout(pnlClientArea.GetProxy(), EDGE_ALIGNED, EDGE_CENTERED, EDGE_ALIGNED, EDGE_ALIGNED )
  188. Case SCROLLPANEL_HSCALING|SCROLLPANEL_VSCALING
  189. SetGadgetLayout(pnlClientArea.GetProxy(), EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED )
  190. EndSelect
  191. HideGadget(scrHorizontal);HideGadget(scrVertical)
  192. ShowGadget(pnlEntire)
  193. Return Self
  194. EndMethod
  195. Method SetShape(x,y,w,h)
  196. Super.SetShape(x,y,w,h)
  197. Update()
  198. EndMethod
  199. Method ClientWidth:Int()
  200. Return pnlViewport.ClientWidth()
  201. EndMethod
  202. Method ClientHeight:Int()
  203. Return pnlViewport.ClientHeight()
  204. EndMethod
  205. Method GetXScroll:Int()
  206. Return currentH
  207. EndMethod
  208. Method GetYScroll:Int()
  209. Return currentV
  210. EndMethod
  211. Method ScrollTo(pHSlider%, pVSlider%)
  212. Local tmpRight:Int = Max( pnlClientArea.ClientWidth()-pnlViewport.ClientWidth(), 0 )
  213. Local tmpBottom:Int = Max( pnlClientArea.ClientHeight()-pnlViewport.ClientHeight(), 0 )
  214. If (pHSlider > tmpRight) Then pHSlider = tmpRight
  215. If (pVSlider > tmpBottom) Then pVSlider = tmpBottom
  216. If (pHSlider >= 0) Then currentH = pHSlider
  217. If (pVSlider >= 0) Then currentV = pVSlider
  218. SetGadgetShape(pnlClientArea.GetProxy(),-currentH,-currentV,pnlClientArea.GetWidth(),pnlClientArea.GetHeight())
  219. EndMethod
  220. Method FitToChildren( pRightMargin:Int = 0, pBottomMargin:Int = 0 )
  221. Local tmpRight:Int, tmpBottom:Int
  222. For Local tmpChild:TGadget = EachIn pnlClientArea.proxy.kids
  223. tmpRight = Max(tmpRight,GadgetX(tmpChild)+GadgetWidth(tmpChild))
  224. tmpBottom = Max(tmpBottom,GadgetY(tmpChild)+GadgetHeight(tmpChild))
  225. Next
  226. HideGadget( pnlViewport )
  227. pnlClientArea.SetShape(0,0,tmpRight + pRightMargin,tmpBottom + pBottomMargin)
  228. ScrollTo(0,0)
  229. ShowGadget( pnlViewport )
  230. EndMethod
  231. Method FitToViewport()
  232. HideGadget( pnlViewport )
  233. pnlClientArea.SetShape(0,0,pnlViewport.ClientWidth(),pnlViewport.ClientHeight())
  234. ScrollTo(0,0)
  235. ShowGadget( pnlViewport )
  236. EndMethod
  237. Method Update()
  238. Local tmpDiff:Int, tmpPos:Int
  239. If Not pnlClientArea Then Return
  240. If pnlViewport.ClientWidth() < pnlClientArea.GetWidth() Then
  241. AddHorizontalScroll(pnlViewport.ClientWidth(),pnlClientArea.GetXPos(),pnlClientArea.GetWidth())
  242. Else
  243. RemoveHorizontalScroll()
  244. EndIf
  245. If pnlViewport.ClientHeight() < pnlClientArea.GetHeight() Then
  246. AddVerticalScroll(pnlViewport.ClientHeight(),pnlClientArea.GetYPos(),pnlClientArea.GetHeight())
  247. Else
  248. RemoveVerticalScroll()
  249. EndIf
  250. If pnlViewport.ClientWidth() < pnlClientArea.GetWidth() Then
  251. tmpDiff = Max(pnlViewport.ClientWidth()-(pnlClientArea.GetXpos() + pnlClientArea.GetWidth()),0)
  252. tmpPos = Min(pnlClientArea.GetXPos()+tmpDiff,0)
  253. AddHorizontalScroll(pnlViewport.ClientWidth(),tmpPos,pnlClientArea.GetWidth())
  254. ScrollTo( -tmpPos, currentV )
  255. Else
  256. RemoveHorizontalScroll()
  257. EndIf
  258. If pnlViewport.ClientHeight() < pnlClientArea.GetHeight() Then
  259. tmpDiff = Max(pnlViewport.ClientHeight()-(pnlClientArea.GetYPos() + pnlClientArea.GetHeight()),0)
  260. tmpPos = Min(pnlClientArea.GetYPos()+tmpDiff,0)
  261. AddVerticalScroll(pnlViewport.ClientHeight(),tmpPos,pnlClientArea.GetHeight())
  262. ScrollTo( currentH, -tmpPos )
  263. Else
  264. RemoveVerticalScroll()
  265. EndIf
  266. EndMethod
  267. Method AddVerticalScroll(pVisible%, pY%, pHeight%)
  268. If scrVertical And Not (flags&SCROLLPANEL_VNEVER) Then
  269. SetGadgetShape(pnlViewport, 0, 0, pnlEntire.ClientWidth() - SCROLL_WIDTH, GadgetHeight(pnlViewport))
  270. SetGadgetShape(scrVertical, pnlEntire.ClientWidth()-SCROLL_WIDTH, 0, SCROLL_WIDTH, GadgetHeight(pnlViewport))
  271. SetSliderRange(scrVertical, pVisible, pHeight);SetSliderValue(scrVertical,-pY)
  272. EnableGadget(scrVertical);ShowGadget(scrVertical)
  273. EndIf
  274. EndMethod
  275. Method RemoveVerticalScroll()
  276. If scrVertical And Not GadgetDisabled(scrVertical) Then
  277. If Not (flags&SCROLLPANEL_VALWAYS) Then
  278. HideGadget(scrVertical)
  279. SetGadgetShape(pnlViewport, 0, 0, pnlEntire.ClientWidth(), GadgetHeight(pnlViewport))
  280. EndIf
  281. DisableGadget(scrVertical)
  282. ScrollTo(currentH,0)
  283. EndIf
  284. EndMethod
  285. Method AddHorizontalScroll(pVisible%, pX%, pWidth%)
  286. If scrHorizontal And Not (flags&SCROLLPANEL_HNEVER) Then
  287. SetGadgetShape(pnlViewport, 0, 0, GadgetWidth(pnlViewport), pnlEntire.ClientHeight()-SCROLL_WIDTH )
  288. SetGadgetShape(scrHorizontal, 0, pnlEntire.ClientHeight()-SCROLL_WIDTH, GadgetWidth(pnlViewport), SCROLL_WIDTH)
  289. SetSliderRange(scrHorizontal, pVisible, pWidth);SetSliderValue(scrHorizontal,-pX)
  290. EnableGadget(scrHorizontal);ShowGadget(scrHorizontal)
  291. EndIf
  292. EndMethod
  293. Method RemoveHorizontalScroll()
  294. If scrHorizontal And Not GadgetDisabled(scrHorizontal) Then
  295. If Not (flags&SCROLLPANEL_HALWAYS) Then
  296. SetGadgetShape(pnlViewport, 0, 0, GadgetWidth(pnlViewport), pnlEntire.ClientHeight())
  297. HideGadget(scrHorizontal)
  298. EndIf
  299. DisableGadget(scrHorizontal)
  300. ScrollTo(0,currentV)
  301. EndIf
  302. EndMethod
  303. Method eventHook:Object(pID%, pData:Object, pContext:Object)
  304. Local tmpEvent:TEvent = TEvent(pData)
  305. If tmpEvent = Null Then Return pData
  306. Select tmpEvent.id
  307. Case EVENT_WINDOWSIZE
  308. If CheckParent(pnlEntire, TGadget(tmpEvent.source)) Then Update()
  309. Case EVENT_GADGETACTION
  310. Local tmpH:Int = currentH, tmpV:Int = currentV
  311. Select tmpEvent.source
  312. Case scrHorizontal
  313. tmpH = SliderValue(scrHorizontal)
  314. Case scrVertical
  315. tmpV = SliderValue(scrVertical)
  316. Default
  317. Return pData
  318. EndSelect
  319. ScrollTo(tmpH, tmpV)
  320. EmitEvent CreateEvent( EVENT_GADGETACTION, Self, 0, 0, currentH, currentV, Null )
  321. pData = Null
  322. EndSelect
  323. Return pData
  324. EndMethod
  325. Method CleanUp()
  326. RemoveHook EmitEventHook, eventHandler, Self
  327. SetProxy(Null)
  328. If pnlClientArea Then pnlClientArea.SetProxy(Null);pnlClientArea = Null
  329. If pnlEntire Then HideGadget(pnlEntire);FreeGadget(pnlEntire)
  330. EndMethod
  331. Function eventHandler:Object(pID%, pData:Object, pContext:Object)
  332. Local tmpSuperPanel:TScrollPanel = TScrollPanel(pContext)
  333. If tmpSuperPanel Then pData = tmpSuperPanel.eventHook(pID%, pData:Object, pContext:Object)
  334. Return pData
  335. EndFunction
  336. Function CheckParent%( pGadget:TGadget, pParentToCheck:TGadget )
  337. If pGadget = pParentToCheck Then Return True
  338. If pGadget.parent Then Return CheckParent(pGadget.parent, pParentToCheck)
  339. EndFunction
  340. EndType
  341. Private
  342. Type TScrollClient Extends TProxyGadget
  343. Method SetShape(x,y,w,h)
  344. Local i:Int, arrDimensions:Int[][], tmpDimensions:Int[]
  345. For Local tmpChild:TGadget = EachIn proxy.kids
  346. tmpDimensions = [GadgetX(tmpChild),GadgetY(tmpChild),GadgetWidth(tmpChild),GadgetHeight(tmpChild)]
  347. arrDimensions:+[tmpDimensions]
  348. Next
  349. Super.SetShape(GetXPos(),GetYPos(),w,h)
  350. TScrollPanel(proxy.parent.parent.source).Update()
  351. For Local tmpChild:TGadget = EachIn proxy.kids
  352. tmpDimensions = arrDimensions[i];i:+1
  353. SetGadgetShape( tmpChild, tmpDimensions[0], tmpDimensions[1], tmpDimensions[2], tmpDimensions[3] )
  354. Next
  355. EndMethod
  356. Method SetLayout(lft,rht,top,bot)
  357. 'Do nothing
  358. EndMethod
  359. EndType