NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

tooltips.py (22573B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\control\tooltips.py
      2 import blue
      3 from carbon.common.script.util.timerstuff import AutoTimer
      4 from carbonui import TextColor, uiconst
      5 from carbonui.text.styles import TextHeader, TextBody, TextDetail
      6 from carbonui.control.contextMenu.menuUtil import GetContextMenuOwner
      7 from carbonui.primitives.container import Container
      8 from carbonui.primitives.containerAutoSize import ContainerAutoSize
      9 from carbonui.primitives.fill import Fill
     10 from carbonui.primitives.sprite import Sprite
     11 from carbonui.uicore import uicore
     12 from eve.client.script.ui import eveColor
     13 from carbonui.control.buttonIcon import ButtonIcon
     14 from eve.client.script.ui.control.infoIcon import InfoIcon
     15 from eve.client.script.ui.control.eveLabel import EveCaptionSmall, EveLabelLarge, EveLabelMedium, EveLabelSmall, EveCaptionMedium, EveCaptionLarge
     16 from eve.client.script.ui.control.pointerPanel import PointerPanel, FrameWithPointer, FadeOutPanelAndClose, RefreshPanelPosition
     17 COLOR_NUMBERVALUE = TextColor.SECONDARY
     18 COLOR_NUMBERVALUE_NEGATIVE = eveColor.HOT_RED
     19 COLOR_NUMBERVALUE_POSITIVE = eveColor.SUCCESS_GREEN
     20 SLEEPTIME_EXTEND = 1000
     21 SLEEPTIME_EXTENDFAST = 10
     22 SLEEPTIME_TIMETOLIVE = 50
     23 SLEEPTIME_TIMETOLIVE_EDITABLE = 300
     24 
     25 class ShortcutHint(Container):
     26     default_align = uiconst.TOPRIGHT
     27 
     28     def ApplyAttributes(self, attributes):
     29         Container.ApplyAttributes(self, attributes)
     30         bgColor = attributes.get('bgColor', (1, 1, 1, 0.1))
     31         Fill(bgParent=self, color=bgColor)
     32         textColor = attributes.get('textColor', TextColor.SECONDARY)
     33         self.textLabel = EveLabelMedium(align=uiconst.CENTER, parent=self, text=attributes.text, bold=True, color=textColor)
     34         self.AdjustSize()
     35 
     36     def AdjustSize(self):
     37         width = self.textLabel.width + 8
     38         self.width = width - width % 16 + 16
     39         self.height = self.textLabel.height
     40 
     41 
     42 class TooltipGeneric(Container):
     43     pointerSize = 9
     44     beingDestroyed = None
     45     scaleTransform = None
     46     default_pointerDirection = uiconst.POINT_BOTTOM_2
     47     horizontal_padding = 16
     48     vertical_padding = 16
     49 
     50     def ApplyAttributes(self, attributes):
     51         attributes.align = uiconst.NOALIGN
     52         Container.ApplyAttributes(self, attributes)
     53         self.pointerDirection = attributes.get('pointerDirection', self.default_pointerDirection)
     54         self.backgroundFrame = FrameWithPointer(bgParent=self)
     55         self.textLabel = EveLabelMedium(align=uiconst.TOPLEFT, width=200, autoFitToText=True, parent=self, left=self.horizontal_padding, top=self.vertical_padding)
     56 
     57     def SetTooltipString(self, tooltipString, owner):
     58         if not tooltipString and self.opacity:
     59             self.display = False
     60             return
     61         self.textLabel.text = tooltipString
     62         self.owner = owner
     63         self.pos = (0,
     64          0,
     65          uicore.ReverseScaleDpi(self.textLabel.actualTextWidth) + 2 * self.horizontal_padding,
     66          uicore.ReverseScaleDpi(self.textLabel.actualTextHeight) + 2 * self.vertical_padding)
     67         RefreshPanelPosition(self)
     68 
     69     @apply
     70     def left():
     71         doc = 'x-coordinate of UI element'
     72 
     73         def fget(self):
     74             return self._left
     75 
     76         def fset(self, value):
     77             self._left = value
     78             self.displayX = uicore.ScaleDpiF(self._left)
     79             ro = self.renderObject
     80             if ro:
     81                 ro.displayX = self._displayX
     82 
     83         return property(**locals())
     84 
     85     @apply
     86     def top():
     87         doc = 'y-coordinate of UI element'
     88 
     89         def fget(self):
     90             return self._top
     91 
     92         def fset(self, value):
     93             self._top = value
     94             self.displayY = uicore.ScaleDpiF(self._top)
     95             ro = self.renderObject
     96             if ro:
     97                 ro.displayY = self._displayY
     98 
     99         return property(**locals())
    100 
    101     @apply
    102     def pos():
    103         doc = 'Position of UI element'
    104 
    105         def fget(self):
    106             return (self._left,
    107              self._top,
    108              self._width,
    109              self._height)
    110 
    111         def fset(self, value):
    112             left, top, width, height = value
    113             doFlag = self._width != width or self._height != height
    114             self._left = left
    115             self._top = top
    116             self._width = width
    117             self._height = height
    118             self.displayX = uicore.ScaleDpiF(self._left)
    119             self.displayY = uicore.ScaleDpiF(self._top)
    120             self.displayWidth = uicore.ScaleDpiF(self._width)
    121             self.displayHeight = uicore.ScaleDpiF(self._height)
    122             ro = self.renderObject
    123             if ro:
    124                 ro.displayX = self._displayX
    125                 ro.displayY = self._displayY
    126                 ro.displayWidth = self._displayWidth
    127                 ro.displayHeight = self._displayHeight
    128                 if not self._constructingBase and doFlag:
    129                     self.FlagAlignmentDirty()
    130 
    131         return property(**locals())
    132 
    133     def _SetDisplay(self, value):
    134         if value != self._display:
    135             self._display = value
    136             ro = self.renderObject
    137             if ro:
    138                 ro.display = value
    139 
    140     def Close(self, *args):
    141         if getattr(uicore.uilib, 'tooltipHandler', None):
    142             now = blue.os.GetWallclockTime()
    143             uicore.uilib.tooltipHandler.lastCloseTime = now
    144         Container.Close(self, *args)
    145         if getattr(self, 'debugFrame', None):
    146             self.debugFrame.Close()
    147         if self.scaleTransform:
    148             self.scaleTransform.Close()
    149             self.scaleTransform = None
    150 
    151 
    152 class TooltipPanel(PointerPanel):
    153     default_state = uiconst.UI_PICKCHILDREN
    154     default_align = uiconst.TOPLEFT
    155     default_opacity = 0.0
    156     default_cellClipChildren = False
    157 
    158     def Close(self, *args):
    159         if getattr(uicore.uilib, 'tooltipHandler', None) and len(self.children):
    160             now = blue.os.GetWallclockTime()
    161             uicore.uilib.tooltipHandler.lastCloseTime = now
    162         if self.owner and getattr(self.owner, 'OnTooltipPanelClosed', None):
    163             self.owner.OnTooltipPanelClosed()
    164         PointerPanel.Close(self, *args)
    165 
    166     def CloseWithFade(self, *args):
    167         if getattr(uicore.uilib, 'tooltipHandler', None) and len(self.children):
    168             now = blue.os.GetWallclockTime()
    169             uicore.uilib.tooltipHandler.lastCloseTime = now
    170         FadeOutPanelAndClose(self)
    171 
    172     def ShowPanel(self, owner):
    173         PointerPanel.ShowPanel(self, owner)
    174         if hasattr(owner, 'LoadExtendedTooltipPanel'):
    175             alt = uicore.uilib.Key(uiconst.VK_MENU)
    176             if alt:
    177                 expandSleeptime = SLEEPTIME_EXTENDFAST
    178             else:
    179                 expandSleeptime = SLEEPTIME_EXTEND
    180             self.expandTimer = AutoTimer(expandSleeptime, self.ExpandTooltipPanel, owner)
    181         if self.pickState == uiconst.TR2_SPS_ON:
    182             timeToLive = SLEEPTIME_TIMETOLIVE_EDITABLE
    183         else:
    184             timeToLive = SLEEPTIME_TIMETOLIVE
    185         if self.owner and getattr(self.owner, 'OnTooltipPanelOpened', None):
    186             self.owner.OnTooltipPanelOpened()
    187         self.HoldTillMouseOutside(timeToLive)
    188         if self.destroyed or self.beingDestroyed:
    189             return
    190         if self.opacity:
    191             FadeOutPanelAndClose(self)
    192         else:
    193             self.Close()
    194 
    195     def ExpandTooltipPanel(self, owner):
    196         self.expandTimer = None
    197         if self.destroyed or self.beingDestroyed:
    198             return
    199         if owner.destroyed:
    200             return
    201         owner.LoadExtendedTooltipPanel(self)
    202 
    203     def HoldTillMouseOutside(self, graceTime):
    204         lastOnTime = blue.os.GetWallclockTime()
    205         radialMenuSvc = sm.GetService('radialmenu')
    206         while not self.destroyed:
    207             now = blue.os.GetWallclockTime()
    208             if self.pickState == uiconst.TR2_SPS_ON:
    209                 contextMenuOwner = GetContextMenuOwner()
    210                 if contextMenuOwner and contextMenuOwner.IsUnder(self):
    211                     lastOnTime = now
    212                     blue.synchro.SleepWallclock(5)
    213                     continue
    214                 radialMenuOwner = radialMenuSvc.GetRadialMenuOwner()
    215                 if radialMenuOwner and radialMenuOwner.IsUnder(self):
    216                     lastOnTime = now
    217                     blue.synchro.SleepWallclock(5)
    218                     continue
    219                 mouseCapture = uicore.uilib.GetMouseCapture()
    220                 if mouseCapture and mouseCapture.IsUnder(self):
    221                     lastOnTime = now
    222                     blue.synchro.SleepWallclock(5)
    223                     continue
    224                 ownerPickable = self.IsOwnerPickable()
    225                 if not ownerPickable:
    226                     return False
    227             mouseInside = self.IsMouseInside()
    228             if mouseInside:
    229                 lastOnTime = now
    230                 blue.synchro.SleepWallclock(5)
    231                 continue
    232             blue.synchro.SleepWallclock(1)
    233             if self.destroyed:
    234                 return False
    235             if lastOnTime and blue.os.TimeDiffInMs(lastOnTime, now) > graceTime:
    236                 return False
    237 
    238     def IsOwnerPickable(self):
    239         owner = self.owner
    240         if not owner:
    241             return False
    242         prestate = owner.state
    243         owner.state = uiconst.UI_NORMAL
    244         try:
    245             ol, ot, ow, oh = owner.GetAbsolute()
    246             ol = uicore.ScaleDpiF(ol)
    247             ot = uicore.ScaleDpiF(ot)
    248             ow = uicore.ScaleDpiF(ow)
    249             oh = uicore.ScaleDpiF(oh)
    250             renderObject, pyObject = uicore.uilib.PickScreenPosition(int(ol + ow / 2), int(ot + oh / 2))
    251             if pyObject and pyObject.IsUnder(uicore.layer.menu):
    252                 return True
    253             if pyObject is not owner:
    254                 pickRadius = uicore.ScaleDpiF(getattr(owner, 'pickRadius', 0.0))
    255                 if pickRadius < 0.0:
    256                     pickRadius = min(ow, oh) / 2.0
    257                 if pickRadius > 0.0:
    258                     x_center = ol + ow / 2.0
    259                     y_center = ot + oh / 2.0
    260                     tryPick = ((x_center - pickRadius + 1, y_center),
    261                      (x_center + pickRadius - 1, y_center),
    262                      (x_center, y_center - pickRadius + 1),
    263                      (x_center, y_center + pickRadius - 1))
    264                 else:
    265                     tryPick = ((ol + 1, ot + oh / 2),
    266                      (ol + ow - 1, ot + oh / 2),
    267                      (ol + ow / 2, ot + 1),
    268                      (ol + ow / 2, ot + oh - 1))
    269                 hits = 0
    270                 for x, y in tryPick:
    271                     renderObject, pyObject = uicore.uilib.PickScreenPosition(int(x), int(y))
    272                     if pyObject is owner:
    273                         hits += 1
    274                         if hits == 2:
    275                             return True
    276                     if pyObject and pyObject.IsUnder(uicore.layer.menu):
    277                         return True
    278 
    279         finally:
    280             owner.state = prestate
    281 
    282         return pyObject is owner
    283 
    284     def IsMouseInside(self):
    285         if self.destroyed:
    286             return False
    287         owner = self.owner
    288         if not owner:
    289             return False
    290         mouseOver = uicore.uilib.mouseOver
    291         if mouseOver is owner:
    292             return True
    293         if mouseOver is self or mouseOver.IsUnder(self):
    294             return True
    295         focus = uicore.registry.GetFocus()
    296         if focus and focus.IsUnder(self):
    297             return True
    298         return False
    299 
    300     def LoadTooltip(self, *args):
    301         pass
    302 
    303     def ExpandTooltip(self, owner):
    304         self.expandTimer = None
    305         if self.destroyed or self.beingDestroyed:
    306             return
    307         if owner.destroyed:
    308             return
    309         if hasattr(owner, 'LoadExtendedTooltipPanel'):
    310             owner.LoadExtendedTooltipPanel(self)
    311 
    312     def AddCommandTooltip(self, command):
    313         label = command.GetName()
    314         shortcutStr = command.GetShortcutAsString()
    315         l = self.AddLabelShortcut(label, shortcutStr)
    316         detailedDescription = command.GetDetailedDescription()
    317         d = None
    318         if detailedDescription:
    319             d = self.AddLabelMedium(text=detailedDescription, align=uiconst.TOPLEFT, wrapWidth=200, colSpan=self.columns, color=TextColor.SECONDARY)
    320         return (l, d)
    321 
    322     def AddShortcutCell(self, shortcut):
    323         shortcutObj = ShortcutHint(text=shortcut)
    324         self.AddCell(shortcutObj)
    325         return shortcutObj
    326 
    327     def AddLabelShortcut(self, label, shortcut, bold = True):
    328         self.FillRow()
    329         labelObj = self.AddLabelMedium(text=label, bold=bold, colSpan=self.columns - 1)
    330         if shortcut:
    331             shortcutObj = self.AddShortcutCell(shortcut)
    332         else:
    333             self.AddCell()
    334             shortcutObj = None
    335         return (labelObj, shortcutObj)
    336 
    337     def AddLabelValue(self, label, value, valueColor = COLOR_NUMBERVALUE, wrapWidth = None, opacity = 1.0):
    338         self.FillRow()
    339         labelObj = self.AddLabelMedium(text=label, align=uiconst.CENTERLEFT, bold=True, cellPadding=(0, 0, 16, 0), opacity=opacity)
    340         valueObj = self.AddLabelMedium(text=value, align=uiconst.CENTERRIGHT, color=valueColor, colSpan=self.columns - 1, wrapWidth=wrapWidth, opacity=opacity)
    341         return (labelObj, valueObj)
    342 
    343     def AddSpriteLabel(self, texturePath, label, labelColor = TextColor.NORMAL, bold = False, iconSize = 32, iconColor = Sprite.default_color, iconOffset = -5, labelOffset = 5, mainAlign = uiconst.CENTERLEFT, align = uiconst.TOLEFT, textAlign = uiconst.CENTERLEFT, **keywords):
    344         mainContainer = ContainerAutoSize(align=mainAlign, height=iconSize, width=iconSize)
    345         sprite = Sprite(parent=mainContainer, align=align, width=iconSize, height=iconSize, texturePath=texturePath, left=iconOffset, color=iconColor)
    346         labelCont = ContainerAutoSize(parent=mainContainer, align=align, height=iconSize)
    347         label = EveLabelMedium(parent=labelCont, text=label, color=labelColor, bold=bold, align=textAlign, left=labelOffset, **keywords)
    348         self.AddCell(mainContainer, **keywords)
    349         return (sprite, label)
    350 
    351     def AddIconLabel(self, icon, label, iconSize = 32, padBottom = 0, bold = True, iconColor = Sprite.default_color):
    352         self.FillRow()
    353         iconObj = Sprite(pos=(0,
    354          0,
    355          iconSize,
    356          iconSize), align=uiconst.CENTERLEFT, color=iconColor)
    357         iconObj.LoadIcon(icon, ignoreSize=True)
    358         self.AddCell(iconObj, cellPadding=(-3,
    359          0,
    360          7,
    361          padBottom))
    362         labelObj = self.AddLabelMedium(text=label, align=uiconst.CENTERLEFT, bold=bold, cellPadding=(0,
    363          0,
    364          7,
    365          padBottom))
    366         return (iconObj, labelObj)
    367 
    368     def AddIconLabelValue(self, icon, label, value, valueColor = COLOR_NUMBERVALUE, iconSize = 32):
    369         self.FillRow()
    370         iconObj = Sprite(pos=(0,
    371          0,
    372          iconSize,
    373          iconSize), align=uiconst.CENTERLEFT)
    374         iconObj.LoadIcon(icon, ignoreSize=True)
    375         self.AddCell(iconObj, cellPadding=(-3, 0, 3, 0))
    376         labelObj = self.AddLabelMedium(text=label, align=uiconst.CENTERLEFT, bold=True, cellPadding=(0, 0, 7, 0))
    377         valueObj = self.AddLabelMedium(align=uiconst.CENTERRIGHT, bold=True, color=valueColor, top=1, colSpan=self.columns - 2, cellPadding=(7, 0, 0, 0))
    378         return (iconObj, labelObj, valueObj)
    379 
    380     def AddButtonLabelValue(self, name, buttonTexturePath, buttonFunc = None, buttonArgs = None, buttonSize = 32, buttonOpacity = 1.0, label = '', valueColor = COLOR_NUMBERVALUE):
    381         self.FillRow()
    382         buttonObj = ButtonIcon(name='%s_icon' % name, texturePath=buttonTexturePath, align=uiconst.CENTERLEFT, pos=(0,
    383          0,
    384          buttonSize,
    385          buttonSize), state=uiconst.UI_NORMAL, func=buttonFunc, args=buttonArgs, iconSize=buttonSize, colorType=uiconst.COLORTYPE_UIHILIGHT)
    386         cell = self.AddCell(buttonObj, cellPadding=(0, 0, 0, 0))
    387         labelObj = self.AddLabelMedium(text=label, align=uiconst.CENTERLEFT, bold=False, cellPadding=(4, 0, 7, 0))
    388         valueObj = self.AddLabelMedium(align=uiconst.CENTERRIGHT, bold=True, color=valueColor, top=0, colSpan=self.columns - 2, cellPadding=(7, 0, 0, 0))
    389         return (cell,
    390          buttonObj,
    391          labelObj,
    392          valueObj)
    393 
    394     def AddDivider(self, color = TextColor.DISABLED, cellPadding = None):
    395         self.FillRow()
    396         divider = Fill(align=uiconst.TOTOP, state=uiconst.UI_DISABLED, color=color, height=1, padding=(0, 3, 0, 3))
    397         self.AddCell(divider, colSpan=self.columns, cellPadding=cellPadding)
    398         return divider
    399 
    400     def AddSpacer(self, width = 0, height = 0, colSpan = 1, rowSpan = 1):
    401         spacer = Fill(align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, width=width, height=height, opacity=0.0)
    402         self.AddCell(spacer, colSpan=colSpan, rowSpan=rowSpan)
    403         return spacer
    404 
    405     def AddLabelSmall(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    406         if wrapWidth:
    407             keywords['width'] = wrapWidth
    408             keywords['autoFitToText'] = True
    409         label = EveLabelSmall(state=state, **keywords)
    410         self.AddCell(label, **keywords)
    411         return label
    412 
    413     def AddLabelMedium(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    414         if wrapWidth:
    415             keywords['width'] = wrapWidth
    416             keywords['autoFitToText'] = True
    417         label = EveLabelMedium(state=state, **keywords)
    418         self.AddCell(label, **keywords)
    419         return label
    420 
    421     def AddLabelLarge(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    422         if wrapWidth:
    423             keywords['width'] = wrapWidth
    424             keywords['autoFitToText'] = True
    425         label = EveLabelLarge(state=state, **keywords)
    426         self.AddCell(label, **keywords)
    427         return label
    428 
    429     def AddCaptionSmall(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    430         if wrapWidth:
    431             keywords['width'] = wrapWidth
    432             keywords['autoFitToText'] = True
    433         label = EveCaptionSmall(state=state, **keywords)
    434         self.AddCell(label, **keywords)
    435         return label
    436 
    437     def AddCaptionMedium(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    438         if wrapWidth:
    439             keywords['width'] = wrapWidth
    440             keywords['autoFitToText'] = True
    441         label = EveCaptionMedium(state=state, **keywords)
    442         self.AddCell(label, **keywords)
    443         return label
    444 
    445     def AddCaptionLarge(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    446         if wrapWidth:
    447             keywords['width'] = wrapWidth
    448             keywords['autoFitToText'] = True
    449         label = EveCaptionLarge(state=state, **keywords)
    450         self.AddCell(label, **keywords)
    451         return label
    452 
    453     def AddInfoIcon(self, typeID = None, itemID = None, align = uiconst.TOPRIGHT, **keywords):
    454         infoIcon = InfoIcon(typeID=typeID, itemID=itemID, align=align, left=0, top=0)
    455         self.AddCell(infoIcon, **keywords)
    456         return infoIcon
    457 
    458     def AddMediumHeader(self, labelColSpan = None, **keywords):
    459         row = self.AddHeaderRow()
    460         label = EveLabelMedium(**keywords)
    461         row.AddCell(label, colSpan=(labelColSpan or self.columns), **keywords)
    462         self.FillRow()
    463         return (row, label)
    464 
    465     def AddTextDetailsLabel(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    466         if wrapWidth:
    467             keywords['width'] = wrapWidth
    468             keywords['autoFitToText'] = True
    469         label = TextDetail(state=state, **keywords)
    470         self.AddCell(label, **keywords)
    471         return label
    472 
    473     def AddTextBodyLabel(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    474         if wrapWidth:
    475             keywords['width'] = wrapWidth
    476             keywords['autoFitToText'] = True
    477         label = TextBody(state=state, **keywords)
    478         self.AddCell(label, **keywords)
    479         return label
    480 
    481     def AddTextHeaderLabel(self, state = uiconst.UI_DISABLED, wrapWidth = None, **keywords):
    482         if wrapWidth:
    483             keywords['width'] = wrapWidth
    484             keywords['autoFitToText'] = True
    485         label = TextHeader(state=state, **keywords)
    486         self.AddCell(label, **keywords)
    487         return label
    488 
    489     def AddTextBodyHeading(self, state = uiconst.UI_DISABLED, wrapWidth = None, labelColSpan = None, **keywords):
    490         if wrapWidth:
    491             keywords['width'] = wrapWidth
    492             keywords['autoFitToText'] = True
    493         row = self.AddHeaderRow()
    494         label = TextBody(state=state, **keywords)
    495         row.AddCell(label, colSpan=(labelColSpan or self.columns), **keywords)
    496         self.FillRow()
    497         return (row, label)
    498 
    499     def AddHeaderRow(self):
    500         self.FillRow()
    501         row = self.AddRow(cellPadding=(11, 9))
    502         Fill(bgParent=row, color=(1, 1, 1, 0.1))
    503         return row
    504 
    505     def LoadGeneric1ColumnTemplate(self):
    506         self.columns = 1
    507         self.LoadStandardSpacingOld()
    508 
    509     def LoadGeneric2ColumnTemplate(self):
    510         self.columns = 2
    511         self.LoadStandardSpacingOld()
    512 
    513     def LoadGeneric3ColumnTemplate(self):
    514         self.columns = 3
    515         self.LoadStandardSpacingOld()
    516 
    517     def LoadGeneric4ColumnTemplate(self):
    518         self.columns = 4
    519         self.LoadStandardSpacingOld()
    520 
    521     def LoadGeneric5ColumnTemplate(self):
    522         self.columns = 5
    523         self.LoadStandardSpacingOld()
    524 
    525     def LoadStandardSpacingOld(self):
    526         self.margin = (16, 16, 16, 16)
    527         self.cellPadding = 0
    528         self.cellSpacing = (8, 8)
    529 
    530     def LoadStandardSpacing(self):
    531         self.margin = (16, 16, 16, 16)
    532         self.cellPadding = (8, 0)
    533         self.cellSpacing = (0, 8)
    534 
    535     def SetSpacing(self, margin = (16, 16, 16, 16), cellPadding = (8, 0), cellSpacing = (0, 8)):
    536         self.margin = margin
    537         self.cellPadding = cellPadding
    538         self.cellSpacing = cellSpacing
    539 
    540 
    541 class TooltipPersistentPanel(TooltipPanel):
    542     picktestEnabled = True
    543     checkIfBlocked = False
    544 
    545     def ApplyAttributes(self, attributes):
    546         TooltipPanel.ApplyAttributes(self, attributes)
    547 
    548     def DisablePickTest(self):
    549         self.picktestEnabled = False
    550 
    551     def HoldTillMouseOutside(self, graceTime):
    552         while not self.destroyed and self.owner and not self.owner.destroyed:
    553             if self.picktestEnabled:
    554                 ownerPickable = self.IsOwnerPickable()
    555                 if not ownerPickable:
    556                     self.opacity = 0.0
    557                 else:
    558                     self.opacity = 1.0
    559             blue.synchro.SleepWallclock(5)
    560 
    561     def IsMouseInside(self):
    562         return True
    563 
    564     def Close(self, *args):
    565         PointerPanel.Close(self, *args)