| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- """Undocumented Module"""
- __all__ = ['DirectScrolledListItem', 'DirectScrolledList']
- from pandac.PandaModules import *
- import DirectGuiGlobals as DGG
- from direct.directnotify import DirectNotifyGlobal
- from direct.task.Task import Task
- from DirectFrame import *
- from DirectButton import *
- import string, types
- class DirectScrolledListItem(DirectButton):
- """
- While you are not required to use a DirectScrolledListItem for a
- DirectScrolledList, doing so takes care of the highlighting and
- unhighlighting of the list items.
- """
- notify = DirectNotifyGlobal.directNotify.newCategory("DirectScrolledListItem")
- def __init__(self, parent=None, **kw):
- assert self.notify.debugStateCall(self)
- self.parent = parent
- if kw.has_key("command"):
- self.nextCommand = kw.get("command")
- del kw["command"]
- if kw.has_key("extraArgs"):
- self.nextCommandExtraArgs = kw.get("extraArgs")
- del kw["extraArgs"]
- optiondefs = (
- ('parent', self.parent, None),
- ('command', self.select, None),
- )
- # Merge keyword options with default options
- self.defineoptions(kw, optiondefs)
- DirectButton.__init__(self)
- self.initialiseoptions(DirectScrolledListItem)
- def select(self):
- assert self.notify.debugStateCall(self)
- apply(self.nextCommand, self.nextCommandExtraArgs)
- self.parent.selectListItem(self)
- class DirectScrolledList(DirectFrame):
- notify = DirectNotifyGlobal.directNotify.newCategory("DirectScrolledList")
- def __init__(self, parent = None, **kw):
- assert self.notify.debugStateCall(self)
- self.index = 0
- self.forceHeight = None
- """ If one were to want a scrolledList that makes and adds its items
- as needed, simply pass in an items list of strings (type 'str')
- and when that item is needed, itemMakeFunction will be called
- with the text, the index, and itemMakeExtraArgs. If itemMakeFunction
- is not specified, it will create a DirectFrame with the text."""
- # if 'items' is a list of strings, make a copy for our use
- # so we can modify it without mangling the user's list
- if kw.has_key('items'):
- for item in kw['items']:
- if type(item) != type(''):
- break
- else:
- # we get here if every item in 'items' is a string
- # make a copy
- kw['items'] = kw['items'][:]
- self.nextItemID = 10
- # Inherits from DirectFrame
- optiondefs = (
- # Define type of DirectGuiWidget
- ('items', [], None),
- ('itemsAlign', TextNode.ACenter, DGG.INITOPT),
- ('itemsWordwrap', None, DGG.INITOPT),
- ('command', None, None),
- ('extraArgs', [], None),
- ('itemMakeFunction', None, None),
- ('itemMakeExtraArgs', [], None),
- ('numItemsVisible', 1, self.setNumItemsVisible),
- ('scrollSpeed', 8, self.setScrollSpeed),
- ('forceHeight', None, self.setForceHeight),
- )
- # Merge keyword options with default options
- self.defineoptions(kw, optiondefs)
- # Initialize superclasses
- DirectFrame.__init__(self, parent)
- self.incButton = self.createcomponent("incButton", (), None,
- DirectButton, (self,),
- )
- self.incButton.bind(DGG.B1PRESS, self.__incButtonDown)
- self.incButton.bind(DGG.B1RELEASE, self.__buttonUp)
- self.decButton = self.createcomponent("decButton", (), None,
- DirectButton, (self,),
- )
- self.decButton.bind(DGG.B1PRESS, self.__decButtonDown)
- self.decButton.bind(DGG.B1RELEASE, self.__buttonUp)
- self.itemFrame = self.createcomponent("itemFrame", (), None,
- DirectFrame, (self,),
- )
- for item in self["items"]:
- if item.__class__.__name__ != 'str':
- item.reparentTo(self.itemFrame)
- self.initialiseoptions(DirectScrolledList)
- self.recordMaxHeight()
- #if len(self["items"]) > 0:
- # self.scrollTo(0)
- self.scrollTo(0)
- def setForceHeight(self):
- assert self.notify.debugStateCall(self)
- self.forceHeight = self["forceHeight"]
- def recordMaxHeight(self):
- assert self.notify.debugStateCall(self)
- if self.forceHeight is not None:
- self.maxHeight = self.forceHeight
- else:
- self.maxHeight = 0.0
- for item in self["items"]:
- if item.__class__.__name__ != 'str':
- self.maxHeight = max(self.maxHeight, item.getHeight())
- def setScrollSpeed(self):
- assert self.notify.debugStateCall(self)
- # Items per second to move
- self.scrollSpeed = self["scrollSpeed"]
- if self.scrollSpeed <= 0:
- self.scrollSpeed = 1
- def setNumItemsVisible(self):
- assert self.notify.debugStateCall(self)
- # Items per second to move
- self.numItemsVisible = self["numItemsVisible"]
- def destroy(self):
- assert self.notify.debugStateCall(self)
- taskMgr.remove(self.taskName("scroll"))
- if hasattr(self, "currentSelected"):
- del self.currentSelected
- self.incButton.destroy()
- self.decButton.destroy()
- DirectFrame.destroy(self)
- def selectListItem(self, item):
- assert self.notify.debugStateCall(self)
- if hasattr(self, "currentSelected"):
- self.currentSelected['state']=DGG.NORMAL
- item['state']=DGG.DISABLED
- self.currentSelected=item
- def scrollBy(self, delta):
- assert self.notify.debugStateCall(self)
- # print "scrollBy[", delta,"]"
- return self.scrollTo(self.index + delta)
- def getItemIndexForItemID(self, itemID):
- assert self.notify.debugStateCall(self)
- #for i in range(len(self["items"])):
- # print "buttontext[", i,"]", self["items"][i]["text"]
- if(len(self["items"])==0):
- return 0
- if(type(self["items"][0])!=types.InstanceType):
- print "warning: getItemIndexForItemID: cant find itemID for non-class list items!"
- return 0
- for i in range(len(self["items"])):
- if(self["items"][i].itemID == itemID):
- return i
- print "warning: getItemIndexForItemID: item not found!"
- return 0
- def scrollToItemID(self, itemID, centered=0):
- assert self.notify.debugStateCall(self)
- self.scrollTo(self.getItemIndexForItemID(itemID), centered)
- def scrollTo(self, index, centered=0):
- """ scrolls list so selected index is at top, or centered in box"""
- assert self.notify.debugStateCall(self)
- # print "scrollTo[", index,"] called, len(self[items])=", len(self["items"])," self[numItemsVisible]=", self["numItemsVisible"]
- try:
- self["numItemsVisible"]
- except:
- # RAU hack to kill 27637
- self.notify.info('crash 27637 fixed!')
- return
- numItemsVisible=self["numItemsVisible"]
- numItemsTotal = len(self["items"])
- if(centered):
- self.index = index - (numItemsVisible/2)
- else:
- self.index = index
- # Not enough items to even worry about scrolling,
- # just disable the buttons and do nothing
- if (len(self["items"]) <= numItemsVisible):
- self.incButton['state'] = DGG.DISABLED
- self.decButton['state'] = DGG.DISABLED
- # Hmm.. just reset self.index to 0 and bail out
- self.index = 0
- ret = 0
- else:
- if (self.index <= 0):
- self.index = 0
- self.decButton['state'] = DGG.DISABLED
- self.incButton['state'] = DGG.NORMAL
- ret = 0
- elif (self.index >= (numItemsTotal - numItemsVisible)):
- self.index = numItemsTotal - numItemsVisible
- # print "at list end, ", len(self["items"])," ", self["numItemsVisible"]
- self.incButton['state'] = DGG.DISABLED
- self.decButton['state'] = DGG.NORMAL
- ret = 0
- else:
- self.incButton['state'] = DGG.NORMAL
- self.decButton['state'] = DGG.NORMAL
- ret = 1
- # print "self.index set to ", self.index
- # Hide them all
- for item in self["items"]:
- if item.__class__.__name__ != 'str':
- item.hide()
- # Then show the ones in range, and stack their positions
- upperRange = min(numItemsTotal, numItemsVisible)
- for i in range(self.index, self.index + upperRange):
- item = self["items"][i]
- #print "stacking buttontext[", i,"]", self["items"][i]["text"]
- # If the item is a 'str', then it has not been created (scrolled list is 'as needed')
- # Therefore, use the the function given to make it or just make it a frame
- if item.__class__.__name__ == 'str':
- if self['itemMakeFunction']:
- # If there is a function to create the item
- item = apply(self['itemMakeFunction'], (item, i, self['itemMakeExtraArgs']))
- else:
- item = DirectFrame(text = item,
- text_align = self['itemsAlign'],
- text_wordwrap = self['itemsWordwrap'],
- relief = None)
- #print "str stacking buttontext[", i,"]", self["items"][i]["text"]
- # Then add the newly formed item back into the normal item list
- self["items"][i] = item
- item.reparentTo(self.itemFrame)
- self.recordMaxHeight()
- item.show()
- item.setPos(0, 0, -(i-self.index) * self.maxHeight)
- #print 'height bug tracker: i-%s idx-%s h-%s' % (i, self.index, self.maxHeight)
- if self['command']:
- # Pass any extra args to command
- apply(self['command'], self['extraArgs'])
- return ret
- def makeAllItems(self):
- assert self.notify.debugStateCall(self)
- for i in range(len(self['items'])):
- item = self["items"][i]
- # If the item is a 'str', then it has not been created
- # Therefore, use the the function given to make it or
- # just make it a frame
- print "Making " + str(item)
- if item.__class__.__name__ == 'str':
- if self['itemMakeFunction']:
- # If there is a function to create the item
- item = apply(self['itemMakeFunction'],
- (item, i, self['itemMakeExtraArgs']))
- else:
- item = DirectFrame(text = item,
- text_align = self['itemsAlign'],
- text_wordwrap = self['itemsWordwrap'],
- relief = None)
- # Then add the newly formed item back into the normal item list
- self["items"][i] = item
- item.reparentTo(self.itemFrame)
- self.recordMaxHeight()
- def __scrollByTask(self, task):
- assert self.notify.debugStateCall(self)
- if ((task.time - task.prevTime) < task.delayTime):
- return Task.cont
- else:
- ret = self.scrollBy(task.delta)
- task.prevTime = task.time
- if ret:
- return Task.cont
- else:
- return Task.done
- def __incButtonDown(self, event):
- assert self.notify.debugStateCall(self)
- task = Task(self.__scrollByTask)
- task.delayTime = (1.0 / self.scrollSpeed)
- task.prevTime = 0.0
- task.delta = 1
- self.scrollBy(task.delta)
- taskMgr.add(task, self.taskName("scroll"))
- def __decButtonDown(self, event):
- assert self.notify.debugStateCall(self)
- task = Task(self.__scrollByTask)
- task.delayTime = (1.0 / self.scrollSpeed)
- task.prevTime = 0.0
- task.delta = -1
- self.scrollBy(task.delta)
- taskMgr.add(task, self.taskName("scroll"))
- def __buttonUp(self, event):
- assert self.notify.debugStateCall(self)
- taskMgr.remove(self.taskName("scroll"))
- def addItem(self, item, refresh=1):
- """
- Add this string and extraArg to the list
- """
- assert self.notify.debugStateCall(self)
- if(type(item) == types.InstanceType):
- # cant add attribs to non-classes (like strings & ints)
- item.itemID = self.nextItemID
- self.nextItemID += 1
- self['items'].append(item)
- if type(item) != type(''):
- item.reparentTo(self.itemFrame)
- if refresh:
- self.refresh()
- if(type(item) == types.InstanceType):
- return item.itemID # to pass to scrollToItemID
- def removeItem(self, item, refresh=1):
- """
- Remove this item from the panel
- """
- assert self.notify.debugStateCall(self)
- #print "remove item called", item
- #print "items list", self['items']
- if item in self["items"]:
- #print "removing item", item
- if hasattr(self, "currentSelected") and self.currentSelected is item:
- del self.currentSelected
- self["items"].remove(item)
- if type(item) != type(''):
- item.reparentTo(hidden)
- self.refresh()
- return 1
- else:
- return 0
- def removeAllItems(self, refresh=1):
- """
- Remove this item from the panel
- Warning 2006_10_19 tested only in the trolley metagame
- """
- assert self.notify.debugStateCall(self)
- retval = 0
- #print "remove item called", item
- #print "items list", self['items']
- while len (self["items"]):
- item = self['items'][0]
- #print "removing item", item
- if hasattr(self, "currentSelected") and self.currentSelected is item:
- del self.currentSelected
- self["items"].remove(item)
- if type(item) != type(''):
- #RAU possible leak here, let's try to do the right thing
- #item.reparentTo(hidden)
- item.removeNode()
- retval = 1
- if (refresh):
- self.refresh()
-
- return retval
- def refresh(self):
- """
- Update the list - useful when adding or deleting items
- or changing properties that would affect the scrolling
- """
- assert self.notify.debugStateCall(self)
- self.recordMaxHeight()
- #print "refresh called"
- self.scrollTo(self.index)
- def getSelectedIndex(self):
- assert self.notify.debugStateCall(self)
- return self.index
- def getSelectedText(self):
- assert self.notify.debugStateCall(self)
- return self['items'][self.index]['text']
- """
- from DirectGui import *
- def makeButton(itemName, itemNum, *extraArgs):
- def buttonCommand():
- print itemName, itemNum
- return DirectButton(text = itemName,
- relief = DGG.RAISED,
- frameSize = (-3.5, 3.5, -0.2, 0.8),
- scale = 0.85,
- command = buttonCommand)
- s = scrollList = DirectScrolledList(
- parent = aspect2d,
- relief = None,
- # Use the default dialog box image as the background
- image = DGG.getDefaultDialogGeom(),
- # Scale it to fit around everyting
- image_scale = (0.7, 1, .8),
- # Give it a label
- text = "Scrolled List Example",
- text_scale = 0.06,
- text_align = TextNode.ACenter,
- text_pos = (0, 0.3),
- text_fg = (0, 0, 0, 1),
- # inc and dec are DirectButtons
- # They can contain a combination of text, geometry and images
- # Just a simple text one for now
- incButton_text = 'Increment',
- incButton_relief = DGG.RAISED,
- incButton_pos = (0.0, 0.0, -0.36),
- incButton_scale = 0.1,
- # Same for the decrement button
- decButton_text = 'Decrement',
- decButton_relief = DGG.RAISED,
- decButton_pos = (0.0, 0.0, 0.175),
- decButton_scale = 0.1,
- # each item is a button with text on it
- numItemsVisible = 4,
- itemMakeFunction = makeButton,
- items = ['Able', 'Baker', 'Charlie', 'Delta', 'Echo', 'Foxtrot',
- 'Golf', 'Hotel', 'India', 'Juliet', 'Kilo', 'Lima'],
- # itemFrame is a DirectFrame
- # Use it to scale up or down the items and to place it relative
- # to eveything else
- itemFrame_pos = (0, 0, 0.06),
- itemFrame_scale = 0.1,
- itemFrame_frameSize = (-3.1, 3.1, -3.3, 0.8),
- itemFrame_relief = DGG.GROOVE,
- )
- """
|