00001 import BigWorld, GUI, Keys
00002 import Utils
00003
00004 from PyGUIBase import PyGUIBase
00005
00006 ITEM_MARGIN = 0.05
00007
00008
00009
00010
00011 class ScrollingList( PyGUIBase ):
00012
00013 factoryString="PyGUI.ScrollingList"
00014
00015 def __init__( self, component ):
00016 PyGUIBase.__init__( self, component )
00017 self.selection = 0
00018 self.itemGuiName = ""
00019 self.backFn = None
00020 self.budget = Utils.Budget( self.createItem, self.deleteItem )
00021 self.selectItemCallback = lambda: None
00022 self.maxVisibleItems = 0
00023 self.totalHeightScreenClip = 0.0
00024 component.focus = True
00025 component.crossFocus = True
00026
00027 def active( self, state ):
00028 if state == self.isActive:
00029 return
00030 PyGUIBase.active( self, state )
00031 self.selectItem( self.selection )
00032
00033
00034
00035 def onSelect( self, pageControl ):
00036 pass
00037
00038
00039
00040 def createItem( self ):
00041 assert self.itemGuiName != ""
00042 g = GUI.load( self.itemGuiName )
00043 setattr( self.items, "m%d"%(len(self.items.children),), g)
00044 g.script.doLayout( self )
00045 return g
00046
00047
00048 def deleteItem( self, c ):
00049 self.items.delChild(c)
00050
00051 def setupItems( self, backFn, setupParams ):
00052 self.backFn = backFn
00053
00054 oldidx = -1
00055 if self.selection < len( self.items.children ):
00056 oldidx = self.selection
00057
00058 self.budget.balance( len(setupParams) )
00059
00060 num = len( setupParams )
00061 for i in xrange( 0, num ):
00062 g=self.items.children[i][1]
00063 g.script.setup(setupParams[i], i)
00064 g.script.select(0)
00065
00066 if num > 0:
00067 self.doLayout( None )
00068 if oldidx >= 0:
00069 self.selectItem( oldidx )
00070
00071 def doLayout( self, parent ):
00072 PyGUIBase.doLayout( self, parent )
00073
00074
00075
00076 y = 1.0
00077 totalHeight = 0
00078 itemHeight = 0
00079 screenWidth = BigWorld.screenWidth()
00080 for discard, i in self.component.items.children:
00081 i.position.y = y
00082 thisItemHeight = i.script.adjustFont( screenWidth )
00083 if itemHeight == 0 and thisItemHeight != 0:
00084 itemHeight = thisItemHeight
00085 y = y - itemHeight - ITEM_MARGIN
00086 totalHeight += itemHeight + ITEM_MARGIN
00087
00088 self.component.items.height = totalHeight
00089
00090
00091
00092 heightMode = self.component.items.heightMode
00093 self.component.items.heightMode = "LEGACY"
00094 self.totalHeightScreenClip = float( self.component.items.height )
00095 self.component.items.heightMode = heightMode
00096
00097
00098 self.items.script.maxScroll[1] = max(0,self.totalHeightScreenClip-self.component.height)
00099 self.items.script.minScroll[1] = 0
00100 if self.items.script.maxScroll[1] == 0.0:
00101 self.scrollUp.visible = 0
00102 self.scrollDown.visible = 0
00103
00104 self.maxVisibleItems = int(2.0 / itemHeight) if itemHeight > 0 else 1
00105
00106
00107
00108
00109
00110 def canSelect( self, idx ):
00111 if idx < 0:
00112 return 0
00113 if idx >= len( self.items.children ):
00114 return 0
00115 return self.items.children[idx][1].script.canSelect()
00116
00117
00118
00119
00120
00121
00122 def moveSelection( self, dist ):
00123 self.selectItem( self.getSelectionOffset(dist) )
00124
00125
00126
00127
00128
00129 def scrollList( self, dist ):
00130 self.items.script.scrollBy( 0, 0.1 * dist )
00131
00132
00133 self.scrollUp.visible = self.items.script.canScrollUp()
00134 self.scrollDown.visible = self.items.script.canScrollDown()
00135
00136
00137
00138
00139
00140
00141 def getSelectionOffset( self, dist ):
00142 curIdx = self.selection
00143
00144 if dist == 0:
00145 return curIdx
00146
00147 newIdx = (curIdx + dist) % len( self.items.children )
00148 direction = -1 if dist < 0 else 1
00149
00150 while not self.canSelect( newIdx ):
00151 newIdx = (newIdx + direction) % len( self.items.children )
00152 if newIdx == curIdx:
00153 return newIdx
00154
00155 return newIdx
00156
00157
00158
00159
00160 def selectItem( self, idx = 0, bringIntoView=True, animate=True, forceReselect=False ):
00161 num = len( self.items.children )
00162 if num == 0 or (idx == self.selection and not forceReselect):
00163 return
00164
00165 oldIdx = self.selection
00166
00167 if idx >= num:
00168 idx = num - 1
00169 if idx < 0:
00170 idx = 0
00171 self.selection = idx
00172
00173 if oldIdx >= 0 and oldIdx < num:
00174 self.items.children[oldIdx][1].script.select(0)
00175 self.items.children[idx][1].script.select(1)
00176
00177 if bringIntoView:
00178 self.checkSelectionVisible(animate)
00179
00180 self.updateControlBar()
00181
00182 try:
00183 self.selectItemCallback( idx )
00184 except Exception, e:
00185 print "ERROR: ScrollingList.selectItem callback", e
00186
00187 def executeSelected( self, playSound = True ):
00188 BigWorld.playSound("ui/boop")
00189 entry = self.items.children[self.selection][1]
00190 i = entry.script.onSelect( self )
00191 self.updateControlBar()
00192
00193
00194
00195
00196
00197 def updateControlBar( self ):
00198 pass
00199
00200
00201
00202
00203
00204
00205 def checkSelectionVisible( self, animate = True ):
00206 if self.items.script.maxScroll[1] == 0.0:
00207 self.items.script.scrollTo(0, 0, animate)
00208 self.scrollUp.visible = 0
00209 self.scrollDown.visible = 0
00210 return
00211
00212 self.scrollToItem( self.selection, animate )
00213
00214
00215
00216
00217
00218 def scrollToItem( self, idx, animate = True ):
00219
00220 itemHeight = (self.totalHeightScreenClip / len(self.component.items.children))
00221 itemScrollY = itemHeight * idx
00222
00223
00224 currentScroll = self.items.script.scroll[1]
00225
00226 if itemScrollY < currentScroll + itemHeight:
00227
00228 scrollTarget = itemScrollY - itemHeight
00229 elif itemScrollY > currentScroll + self.component.height - itemHeight * 2.0:
00230
00231 scrollTarget = itemScrollY - self.component.height + itemHeight * 2.0
00232 else:
00233
00234 scrollTarget = currentScroll
00235
00236 self.items.script.scrollTo(0, scrollTarget, animate)
00237
00238
00239 self.scrollUp.visible = self.items.script.canScrollUp()
00240 self.scrollDown.visible = self.items.script.canScrollDown()
00241
00242
00243
00244
00245
00246 def handleTraversalKeys( self, down, key, modifiers ):
00247 if not down:
00248 return 0
00249
00250 if key in [Keys.KEY_JOYDDOWN,Keys.KEY_DOWNARROW,Keys.KEY_S]:
00251 if len( self.items.children ) == 0:
00252 return 1
00253 oidx = None
00254 idx = self.selection
00255 while idx != oidx:
00256 if oidx == None: oidx = idx
00257 idx += 1
00258 if idx >= len( self.items.children ):
00259 idx = 0
00260 if self.canSelect( idx ):
00261 self.selectItem( idx )
00262 break
00263 BigWorld.playSound("ui/tick")
00264 return 1
00265 elif key in [Keys.KEY_JOYDUP,Keys.KEY_UPARROW,Keys.KEY_W]:
00266 if len( self.items.children ) == 0:
00267 return 1
00268 oidx = None
00269 idx = self.selection
00270 while idx != oidx:
00271 if oidx == None: oidx = idx
00272 idx -= 1
00273 if idx < 0:
00274 idx = len( self.items.children ) - 1
00275 if self.canSelect( idx ):
00276 self.selectItem( idx )
00277 break
00278 BigWorld.playSound("ui/tick")
00279 return 1
00280 elif key == Keys.KEY_PGUP:
00281 dist = self.maxVisibleItems
00282 if self.selection - dist < 0:
00283 dist = self.selection
00284 self.moveSelection( -dist )
00285 elif key == Keys.KEY_PGDN:
00286 dist = self.maxVisibleItems
00287 if self.selection + dist >= len( self.items.children ):
00288 dist = len( self.items.children ) - self.selection - 1
00289 self.moveSelection( dist )
00290 elif key == Keys.KEY_HOME:
00291 self.selectItem(0)
00292 elif key == Keys.KEY_END:
00293 self.selectItem( len( self.items.children )-1 )
00294
00295 return 0
00296
00297
00298
00299
00300 def handleKeyEvent( self, down, key, modifiers ):
00301 if ( down ):
00302 if self.handleTraversalKeys( down, key, modifiers ):
00303 return 1
00304 elif key == [Keys.KEY_JOYA] or (
00305 key == Keys.KEY_RETURN and
00306 not BigWorld.isKeyDown( Keys.KEY_LALT ) and
00307 not BigWorld.isKeyDown( Keys.KEY_RALT )):
00308 if len( self.items.children ) == 0:
00309 return 1
00310
00311
00312 BigWorld.sinkKeyEvents( Keys.KEY_RETURN )
00313 self.executeSelected()
00314 return 1
00315 elif key in [Keys.KEY_JOYB,Keys.KEY_JOYBACK,Keys.KEY_ESCAPE, \
00316 Keys.KEY_BACKSPACE]:
00317 if self.backFn != None:
00318 self.active( 0 )
00319 BigWorld.playSound("ui/boop")
00320 self.backFn()
00321 return 1
00322 return 0
00323
00324 def onLoad( self, section ):
00325 self.itemGuiName = section.readString( "itemGui", "")
00326 assert self.itemGuiName != ""
00327
00328
00329
00330
00331
00332 def onBound( self ):
00333 PyGUIBase.onBound( self )
00334 try:
00335 self.items = self.component.items
00336 except:
00337 print "the scrolling list should have a items area!!!"
00338
00339 if hasattr( self.component, 'scrollUp' ):
00340 self.scrollUp = self.component.scrollUp
00341 if hasattr( self.component, 'scrollDown' ):
00342 self.scrollDown = self.component.scrollDown
00343
00344