defensePanel.py (21820B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\fittingScreen\fittingPanels\defensePanel.py 2 import math 3 import dogma.const 4 import dogma.data 5 from carbon.common.script.util.format import FmtAmt 6 from carbonui import uiconst 7 from carbonui.primitives.container import Container 8 from carbonui.primitives.fill import Fill 9 from carbonui.text.settings import check_convert_font_size 10 from carbonui.uianimations import animations 11 from carbonui.util.various_unsorted import IsUnder, GetAttrs 12 from carbonui.window.underlay import WindowUnderlay 13 from eve.client.script.ui.control.damageGaugeContainers import DamageGaugeContainerFitting 14 from eve.client.script.ui.control.eveIcon import Icon 15 from eve.client.script.ui.control.eveLabel import EveLabelMedium, Label 16 from eve.client.script.ui.shared.fitting.fittingUtil import PASSIVESHIELDRECHARGE, ARMORREPAIRRATEACTIVE, HULLREPAIRRATEACTIVE, SHIELDBOOSTRATEACTIVE, FONTCOLOR_DEFAULT2, GetDefensiveLayersInfo, GetShipAttributeWithDogmaLocation, PANEL_WIDTH_RIGHT 17 from eve.client.script.ui.shared.fittingScreen.fittingUtil import GetColoredText 18 from eve.client.script.ui.station.fitting.fittingTooltipUtils import SetFittingTooltipInfo 19 from eve.client.script.ui.shared.fittingScreen.fittingPanels.basePanel import BaseMenuPanel 20 from eve.common.lib.appConst import SEC 21 from inventorycommon.const import fittingFlags, groupArmorRepairUnit, groupFueledArmorRepairer, groupFueledShieldBooster, groupHullRepairUnit, groupShieldBooster 22 from localization import GetByLabel, GetByMessageID 23 from carbonui.uicore import uicore 24 MAXDEFENCELABELWIDTH = 62 25 MAXDEFENCELABELHEIGHT = 34 26 ACTIVE_DEFENSES_TOOLTIP_NAME = 'ActiveDefenses' 27 MILLION = 1000000 28 SECONDS_DAY = 86400 29 ICON_SIZE = 20 30 resAttributeIDs = ((dogma.const.attributeEmDamageResonance, 'ResistanceHeaderEM', 'res:/UI/Texture/classes/Fitting/StatsIcons/EMResistance.png'), 31 (dogma.const.attributeThermalDamageResonance, 'ResistanceHeaderThermal', 'res:/UI/Texture/classes/Fitting/StatsIcons/thermalResistance.png'), 32 (dogma.const.attributeKineticDamageResonance, 'ResistanceHeaderKinetic', 'res:/UI/Texture/classes/Fitting/StatsIcons/kineticResistance.png'), 33 (dogma.const.attributeExplosiveDamageResonance, 'ResistanceHeaderExplosive', 'res:/UI/Texture/classes/Fitting/StatsIcons/explosiveResistance.png')) 34 rowsInfo = [('UI/Common/Shield', 'shield', 'res:/UI/Texture/classes/Fitting/StatsIcons/shieldHP.png', 'UI/Fitting/FittingWindow/ShieldHPAndRecharge'), ('UI/Common/Armor', 'armor', 'res:/UI/Texture/classes/Fitting/StatsIcons/armorHP.png', 'UI/Common/Armor'), ('UI/Fitting/Structure', 'structure', 'res:/UI/Texture/classes/Fitting/StatsIcons/structureHP.png', 'UI/Fitting/Structure')] 35 DATASET_FOR_REPAIRERS = {ARMORREPAIRRATEACTIVE: ('UI/Fitting/FittingWindow/ArmorRepairRate', 36 (groupArmorRepairUnit, groupFueledArmorRepairer), 37 dogma.const.attributeArmorDamageAmount, 38 dogma.const.attributeChargedArmorDamageMultiplier, 39 'res:/UI/Texture/classes/Fitting/StatsIcons/armorRepairRate.png'), 40 HULLREPAIRRATEACTIVE: ('UI/Fitting/FittingWindow/HullRepairRate', 41 (groupHullRepairUnit,), 42 dogma.const.attributeStructureDamageAmount, 43 None, 44 'res:/UI/Texture/classes/Fitting/StatsIcons/hullRepairRate.png'), 45 SHIELDBOOSTRATEACTIVE: ('UI/Fitting/FittingWindow/ShieldBoostRate', 46 (groupShieldBooster, groupFueledShieldBooster), 47 dogma.const.attributeShieldBonus, 48 None, 49 'res:/UI/Texture/classes/Fitting/StatsIcons/shieldBoostRate.png'), 50 PASSIVESHIELDRECHARGE: ('UI/Fitting/FittingWindow/PassiveShieldRecharge', None, None, None, 'res:/UI/Texture/classes/Fitting/StatsIcons/passiveShieldRecharge.png')} 51 52 class DefensePanel(BaseMenuPanel): 53 col1Width = 90 54 55 def ApplyAttributes(self, attributes): 56 BaseMenuPanel.ApplyAttributes(self, attributes) 57 58 def LoadPanel(self, initialLoad = False): 59 self.Flush() 60 self.ResetStatsDicts() 61 self.display = True 62 tRow = Container(name='topRow', parent=self, align=uiconst.TOTOP, height=28, padTop=5, padLeft=3) 63 self.AddBestRepairPicker(tRow) 64 self.AddColumnHeaderIcons(tRow) 65 for idx in xrange(len(rowsInfo)): 66 self.AddRow(idx) 67 68 BaseMenuPanel.FinalizePanelLoading(self, initialLoad) 69 70 def AddBestRepairPicker(self, tRow): 71 self.bestRepairPickerPanel = None 72 bestPar = Container(name='bestPar', parent=tRow, align=uiconst.TOPLEFT, height=32, width=self.col1Width, state=uiconst.UI_NORMAL) 73 bestPar.OnClick = self.ExpandBestRepair 74 SetFittingTooltipInfo(targetObject=bestPar, tooltipName=ACTIVE_DEFENSES_TOOLTIP_NAME) 75 self.expandIcon = Icon(name='expandIcon', icon='ui_38_16_229', parent=bestPar, state=uiconst.UI_DISABLED, align=uiconst.TOPLEFT, rotation=math.pi, pos=(-5, -5, 0, 0)) 76 numPar = Container(name='numPar', parent=bestPar, pos=(0, 0, 11, 11), align=uiconst.BOTTOMLEFT, state=uiconst.UI_DISABLED) 77 numLabel = EveLabelMedium(text='', parent=numPar, atop=-1, state=uiconst.UI_DISABLED, align=uiconst.CENTER, shadowOffset=(0, 0)) 78 Fill(parent=numPar, color=(0.0, 0.0, 0.0, 0.5)) 79 self.activeBestRepairNumLabel = numLabel 80 icon = Icon(parent=bestPar, state=uiconst.UI_DISABLED, width=ICON_SIZE, height=ICON_SIZE, align=uiconst.CENTERLEFT) 81 statusLabel = Label(name='statusLabel', text='', parent=bestPar, left=icon.left + icon.width, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT) 82 statusLabel.SetRightAlphaFade(fadeEnd=self.col1Width - 20, maxFadeWidth=15) 83 self.activeBestRepairLabel = statusLabel 84 self.activeBestRepairParent = bestPar 85 self.activeBestRepairIcon = icon 86 87 def AddColumnHeaderIcons(self, tRow): 88 step = (PANEL_WIDTH_RIGHT - self.col1Width - 10) / 4.0 89 left = self.col1Width 90 counter = 0 91 for attributeID, tooltipName, texturePath in resAttributeIDs: 92 contLeft = left + int(counter * step) 93 cont = Container(parent=tRow, align=uiconst.TOLEFT_NOPUSH, pos=(contLeft, 94 0, 95 int(step), 96 0)) 97 icon = Icon(parent=cont, pos=(-6, 0, 24, 24), idx=0, hint=dogma.data.get_attribute_display_name(attributeID), icon=texturePath, align=uiconst.CENTER) 98 SetFittingTooltipInfo(icon, tooltipName=tooltipName, includeDesc=True) 99 counter += 1 100 101 def AddRow(self, idx): 102 labelPath, what, iconNo, labelHintPath = rowsInfo[idx] 103 rowName = 'row_%s' % what 104 row = Container(name='row_%s' % rowName, parent=self, align=uiconst.TOTOP, height=32, padLeft=3) 105 mainIcon = Icon(icon=iconNo, parent=row, pos=(0, 106 0, 107 ICON_SIZE, 108 ICON_SIZE), ignoreSize=True, align=uiconst.CENTERLEFT) 109 mainIcon.hint = GetByLabel(labelPath) 110 statusLabel = Label(text='', parent=row, left=mainIcon.left + mainIcon.width, state=uiconst.UI_NORMAL, align=uiconst.CENTERLEFT, width=62) 111 statusLabel.hint = GetByLabel(labelHintPath) 112 self.statsLabelsByIdentifier[what] = statusLabel 113 dmgContainer = Container(parent=row, name='dmgContainer', left=self.col1Width) 114 gaugeCont = DamageGaugeContainerFitting(parent=dmgContainer) 115 self.statsContsByIdentifier[what] = gaugeCont 116 117 def SetEffectiveHpHeader(self): 118 effectiveHpInfo = self.controller.GetEffectiveHp() 119 text = GetByLabel('UI/Fitting/FittingWindow/ColoredEffectiveHp', effectiveHp=int(effectiveHpInfo.value)) 120 coloredText = GetColoredText(isBetter=effectiveHpInfo.isBetterThanBefore, text=text) 121 self.SetStatusText(coloredText) 122 123 def UpdateDefensePanel(self): 124 self.SetEffectiveHpHeader() 125 allDefensiveLayersInfo = GetDefensiveLayersInfo(self.controller) 126 for whatLayer, layerInfo in allDefensiveLayersInfo.iteritems(): 127 hpInfo = layerInfo.hpInfo 128 isRechargable = layerInfo.isRechargable 129 layerResistancesInfo = layerInfo.resistances 130 if not hpInfo: 131 continue 132 self.SetDefenseLayerText(hpInfo, whatLayer, isRechargable) 133 self.UpdateGaugesForLayer(layerResistancesInfo, whatLayer) 134 135 def UpdateGaugesForLayer(self, layerResistancesInfo, whatLayer): 136 dmgGaugeCont = self.statsContsByIdentifier.get(whatLayer, None) 137 for dmgType, valueInfo in layerResistancesInfo.iteritems(): 138 value = valueInfo.value 139 if self.state != uiconst.UI_HIDDEN and dmgGaugeCont: 140 text = GetByLabel('UI/Fitting/FittingWindow/ColoredResistanceLabel', number=100 - int(value * 100)) 141 coloredText = GetColoredText(isBetter=valueInfo.isBetterThanBefore, text=text) 142 attributeInfo = dogma.data.get_attribute(valueInfo.attributeID) 143 tooltipTitleID = attributeInfo.tooltipTitleID 144 if tooltipTitleID: 145 tooltipText = GetByMessageID(attributeInfo.tooltipTitleID) 146 else: 147 tooltipText = dogma.data.get_attribute_display_name(valueInfo.attributeID) 148 info = {'value': 1.0 - value, 149 'valueText': coloredText, 150 'text': tooltipText, 151 'dmgType': dmgType, 152 'attributeID': valueInfo.attributeID} 153 dmgGaugeCont.UpdateGauge(info, animate=True) 154 155 def SetDefenseLayerText(self, statusInfo, what, isRechargable): 156 label = self.statsLabelsByIdentifier.get(what, None) 157 if not label: 158 return 159 hpText = self._GetFormattedValue(statusInfo.value) 160 text = None 161 if isRechargable: 162 rechargeTimeInfo = self.controller.GetRechargeRate() 163 rechargeValue = rechargeTimeInfo.value 164 if rechargeValue < SECONDS_DAY * SEC: 165 text = GetByLabel('UI/Fitting/FittingWindow/ColoredHitpointsAndRechargeTime2', hp=hpText, rechargeTime=int(rechargeValue * 0.001), startColorTag1='', startColorTag2='', endColorTag='') 166 if text is None: 167 text = GetByLabel('UI/Fitting/FittingWindow/ColoredHp2', hp=hpText) 168 coloredText = GetColoredText(isBetter=statusInfo.isBetterThanBefore, text=text) 169 maxTextHeight = MAXDEFENCELABELHEIGHT 170 maxTextWidth = MAXDEFENCELABELWIDTH 171 textWidth, textHeight = label.MeasureTextSize(coloredText) 172 fontsize = check_convert_font_size(label.default_fontsize) 173 lineSpacing = -0.2 174 while textWidth > maxTextWidth or textHeight > maxTextHeight: 175 fontsize -= 1 176 textWidth, textHeight = label.MeasureTextSize(coloredText, fontsize=fontsize, lineSpacing=lineSpacing) 177 178 label.fontsize = fontsize 179 label.lineSpacing = lineSpacing 180 label.text = coloredText 181 182 def _GetFormattedValue(self, value): 183 if value > 100 * MILLION: 184 fmt = 'sn' 185 else: 186 fmt = 'ln' 187 valueText = FmtAmt(int(value), fmt=fmt) 188 return valueText 189 190 def ExpandBestRepair(self, *args): 191 if self.bestRepairPickerPanel is not None: 192 self.PickBestRepair(None) 193 return 194 self.sr.bestRepairPickerCookie = uicore.event.RegisterForTriuiEvents(uiconst.UI_MOUSEUP, self.OnGlobalMouseUp_BestRepair) 195 bestRepairParent = self.activeBestRepairParent 196 l, t, w, h = bestRepairParent.GetAbsolute() 197 bestRepairPickerPanel = Container(parent=uicore.desktop, name='bestRepairPickerPanel', align=uiconst.TOPLEFT, width=150, height=100, left=l, top=t + h, state=uiconst.UI_NORMAL, idx=0, clipChildren=1) 198 subpar = Container(parent=bestRepairPickerPanel, name='subpar', align=uiconst.TOALL, state=uiconst.UI_PICKCHILDREN, pos=(0, 0, 0, 0)) 199 active = settings.user.ui.Get('activeBestRepair', PASSIVESHIELDRECHARGE) 200 top = 0 201 mw = 32 202 for flag in (ARMORREPAIRRATEACTIVE, 203 HULLREPAIRRATEACTIVE, 204 PASSIVESHIELDRECHARGE, 205 SHIELDBOOSTRATEACTIVE): 206 hintPath, _, _, _, iconNo = self.GetDataSetForRepairers(flag) 207 hint = GetByLabel(hintPath) 208 entry = Container(name='entry', parent=subpar, align=uiconst.TOTOP, height=32, state=uiconst.UI_NORMAL) 209 icon = Icon(icon=iconNo, parent=entry, state=uiconst.UI_DISABLED, pos=(2, 210 0, 211 ICON_SIZE, 212 ICON_SIZE), ignoreSize=True, align=uiconst.CENTERLEFT) 213 label = Label(text=hint, parent=entry, left=icon.left + icon.width + 2, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT) 214 entry.OnClick = (self.PickBestRepair, entry) 215 entry.OnMouseEnter = (self.OnMouseEnterBestRepair, entry) 216 entry.bestRepairFlag = flag 217 entry.sr.hilite = Fill(parent=entry, state=uiconst.UI_HIDDEN) 218 if active == flag: 219 Fill(parent=entry, color=(1.0, 1.0, 1.0, 0.125)) 220 top += 32 221 mw = max(label.textwidth + label.left + 6, mw) 222 223 bestRepairPickerPanel.width = mw 224 bestRepairPickerPanel.height = 32 225 bestRepairPickerPanel.opacity = 0.0 226 WindowUnderlay(bgParent=bestRepairPickerPanel) 227 self.bestRepairPickerPanel = bestRepairPickerPanel 228 animations.MorphScalar(bestRepairPickerPanel, 'height', endVal=top, duration=0.25) 229 animations.FadeIn(bestRepairPickerPanel, duration=0.25) 230 self.RotateExpandIconDown() 231 232 def RotateExpandIconDown(self): 233 animations.MorphScalar(self.expandIcon, 'rotation', self.expandIcon.rotation, 0, duration=0.25, curveType=uiconst.ANIM_LINEAR) 234 235 def RotateExpandIconUp(self): 236 animations.MorphScalar(self.expandIcon, 'rotation', self.expandIcon.rotation, math.pi, duration=0.25, curveType=uiconst.ANIM_LINEAR) 237 238 def UpdateBestRepair(self): 239 if not self.panelLoaded: 240 return 241 activeRepairLabel = self.activeBestRepairLabel 242 activeBestRepairParent = self.activeBestRepairParent 243 activeBestRepairNumLabel = self.activeBestRepairNumLabel 244 activeBestRepairIcon = self.activeBestRepairIcon 245 if not activeRepairLabel: 246 return 247 activeBestRepair = settings.user.ui.Get('activeBestRepair', PASSIVESHIELDRECHARGE) 248 if activeBestRepair == PASSIVESHIELDRECHARGE: 249 SetFittingTooltipInfo(activeBestRepairParent, ACTIVE_DEFENSES_TOOLTIP_NAME) 250 return self.UpdatePassiveShieldRecharge() 251 hintPath, groupIDs, repairAttributeID, chargeMultiplierAttributeID, iconNum = self.GetDataSetForRepairers(activeBestRepair) 252 hint = GetByLabel(hintPath) 253 activeBestRepairParent.hint = hint 254 modulesAndCharges = self.FindModulesAndChargesByGroups(groupIDs) 255 if modulesAndCharges: 256 totalHpPerSec = self.GetTotalHpPerSec(modulesAndCharges, repairAttributeID, chargeMultiplierAttributeID) 257 if totalHpPerSec: 258 text = GetByLabel('UI/Fitting/FittingWindow/ColoredPassiveRepairRate', hpPerSec=totalHpPerSec) 259 activeRepairLabel.text = text 260 else: 261 activeRepairLabel.text = 0 262 SetFittingTooltipInfo(activeBestRepairParent, ACTIVE_DEFENSES_TOOLTIP_NAME) 263 text = GetByLabel('UI/Fitting/FittingWindow/ColoredBestRepairNumber', numberOfModules=len(modulesAndCharges)) 264 activeBestRepairNumLabel.text = text 265 activeBestRepairNumLabel.bold = True 266 activeBestRepairNumLabel.parent.state = uiconst.UI_DISABLED 267 else: 268 activeRepairLabel.text = GetByLabel('UI/Fitting/FittingWindow/NoModule') 269 activeBestRepairNumLabel.text = GetByLabel('UI/Fitting/FittingWindow/NoModuleNumber') 270 SetFittingTooltipInfo(activeBestRepairParent, ACTIVE_DEFENSES_TOOLTIP_NAME, extraHeader=GetByLabel('UI/Fitting/FittingWindow/NoModule')) 271 activeBestRepairNumLabel.parent.state = uiconst.UI_DISABLED 272 activeBestRepairIcon.LoadIcon(iconNum, ignoreSize=True) 273 274 def FindModulesAndChargesByGroups(self, groupIDs): 275 modulesAndCharges = [] 276 modulesByFlagID = self.controller.GetFittedModulesByFlagID() 277 for eachFlag in fittingFlags: 278 module = modulesByFlagID.get((eachFlag, False), None) 279 if not module: 280 continue 281 if module.groupID not in groupIDs: 282 continue 283 charge = modulesByFlagID.get((eachFlag, True), None) 284 modulesAndCharges.append((module, charge)) 285 286 return modulesAndCharges 287 288 def GetDataSetForRepairers(self, activeBestRepair): 289 return DATASET_FOR_REPAIRERS[activeBestRepair] 290 291 def GetTextWithColor(self, color, text): 292 activeRepairText = '<color=%s>' % color 293 activeRepairText += text 294 activeRepairText += '</color>' 295 return activeRepairText 296 297 def UpdatePassiveShieldRecharge(self): 298 passiveRechargeRateInfo = self.controller.GetPassiveRechargeRate() 299 activeRepairText = GetByLabel('UI/Fitting/FittingWindow/ColoredPassiveRepairRate', hpPerSec=int(passiveRechargeRateInfo.value)) 300 coloredText = GetColoredText(isBetter=passiveRechargeRateInfo.isBetterThanBefore, text=activeRepairText) 301 self.activeBestRepairLabel.text = coloredText 302 self.activeBestRepairParent.hint = GetByLabel('UI/Fitting/FittingWindow/PassiveShieldRecharge') 303 self.activeBestRepairNumLabel.parent.state = uiconst.UI_HIDDEN 304 _, _, _, _, shieldIconID = self.GetDataSetForRepairers(PASSIVESHIELDRECHARGE) 305 self.activeBestRepairIcon.LoadIcon(shieldIconID, ignoreSize=True) 306 307 def GetTotalHpPerSec(self, modulesAndCharges, repairAttributeID, chargeMultiplierAttributeID = None): 308 hpsAndDurations = [] 309 totalPerSec = 0 310 311 def GetHpPerSec(hpValue, durationValue): 312 return hpValue / (durationValue / 1000.0) 313 314 for module, charges in modulesAndCharges: 315 dogmaItem = self.dogmaLocation.dogmaItems.get(module.itemID, None) 316 include = self.IncludeModuleInHpCalculation(dogmaItem) 317 if not include: 318 continue 319 shipID = self.dogmaLocation.GetCurrentShipID() 320 if dogmaItem and dogmaItem.locationID == shipID: 321 duration = self.dogmaLocation.GetAccurateAttributeValue(dogmaItem.itemID, dogma.const.attributeDuration) 322 hp = self.dogmaLocation.GetAccurateAttributeValue(dogmaItem.itemID, repairAttributeID) 323 multiplier = 1.0 324 if charges and chargeMultiplierAttributeID: 325 multiplier = self.dogmaLocation.GetAccurateAttributeValue(dogmaItem.itemID, chargeMultiplierAttributeID) 326 hp *= multiplier 327 totalPerSec += GetHpPerSec(hp, duration) 328 hpsAndDurations.append((hp, duration)) 329 else: 330 duration = self.dogmaLocation.dogmaStaticMgr.GetTypeAttribute2(module.typeID, dogma.const.attributeDuration) 331 hp = self.dogmaLocation.dogmaStaticMgr.GetTypeAttribute2(module.typeID, repairAttributeID) 332 if charges: 333 multiplier = self.dogmaLocation.dogmaStaticMgr.GetTypeAttribute2(module.typeID, chargeMultiplierAttributeID) 334 hp *= multiplier 335 totalPerSec += GetHpPerSec(hp, duration) 336 337 return totalPerSec 338 339 def IncludeModuleInHpCalculation(self, module): 340 if not sm.GetService('fittingSvc').IsShipSimulated(): 341 return True 342 if not module: 343 return False 344 typeEffectInfo = sm.GetService('ghostFittingSvc').GetDefaultAndOverheatEffect(module.typeID) 345 if not typeEffectInfo.defaultEffect or not typeEffectInfo.isActivatable: 346 return True 347 if not module.IsActive(): 348 return False 349 return True 350 351 def GetShipAttribute(self, attributeID): 352 dogmaLocation = self.controller.GetDogmaLocation() 353 shipID = dogmaLocation.GetCurrentShipID() 354 return GetShipAttributeWithDogmaLocation(dogmaLocation, shipID, attributeID) 355 356 def OnGlobalMouseUp_BestRepair(self, fromwhere, *etc): 357 if self.bestRepairPickerPanel: 358 if uicore.uilib.mouseOver == self.bestRepairPickerPanel or IsUnder(fromwhere, self.bestRepairPickerPanel) or fromwhere == self.activeBestRepairParent: 359 import log 360 log.LogInfo('Combo.OnGlobalClick Ignoring all clicks from comboDropDown') 361 return 1 362 if self.bestRepairPickerPanel and not self.bestRepairPickerPanel.destroyed: 363 self.bestRepairPickerPanel.Close() 364 self.bestRepairPickerPanel = None 365 if self.sr.bestRepairPickerCookie: 366 uicore.event.UnregisterForTriuiEvents(self.sr.bestRepairPickerCookie) 367 self.sr.bestRepairPickerCookie = None 368 self.RotateExpandIconUp() 369 return 0 370 371 def PickBestRepair(self, entry): 372 if entry: 373 settings.user.ui.Set('activeBestRepair', entry.bestRepairFlag) 374 self.controller.UpdateStats() 375 if self.bestRepairPickerPanel and not self.bestRepairPickerPanel.destroyed: 376 self.bestRepairPickerPanel.Close() 377 self.bestRepairPickerPanel = None 378 if self.sr.bestRepairPickerCookie: 379 uicore.event.UnregisterForTriuiEvents(self.sr.bestRepairPickerCookie) 380 self.sr.bestRepairPickerCookie = None 381 self.RotateExpandIconUp() 382 383 def OnMouseEnterBestRepair(self, entry): 384 for each in entry.parent.children: 385 if GetAttrs(each, 'sr', 'hilite'): 386 each.sr.hilite.state = uiconst.UI_HIDDEN 387 388 entry.sr.hilite.state = uiconst.UI_DISABLED