uipointersvc.py (42858B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\services\uipointersvc.py 2 import blue 3 import collections 4 import uthread 5 import sys 6 import log 7 import carbonui.const as uiconst 8 import telemetry 9 import mathext 10 import utillib 11 from carbon.common.script.sys.service import Service 12 from carbon.common.script.util.timerstuff import AutoTimer 13 from carbonui.control.window import Window 14 from carbonui.signals.ui_element_signals import on_unique_ui_element_created, on_unique_ui_element_destroyed 15 from eve.client.script.ui.shared.neocom.neocom import neocomSettings 16 from localization import GetByLabel 17 from uihighlighting.const import UiHighlightDirections 18 from uihighlighting.ui.uiutil import UI_CLASS_BY_NAME, DEFAULT_UI_CLASS 19 from uihighlighting.ui.spaceobjectpointer import SpaceObjectPointer, SpaceObjectUiPointerData 20 from uihighlighting.ui.uipointer import CustomUiPointer, UiPointerData 21 from uihighlighting.ui.uipointer import UIPOINTER_WIDTH, UIPOINTER_HEIGHT, POINTER_ARROW_WIDTH, POINTER_ARROW_HEIGHT 22 from uihighlighting import uihighlightaudio 23 from uihighlighting.statelogger import StateLogger 24 from carbonui.uicore import uicore 25 import eve.client.script.ui.shared.pointerTool.pointerToolConst as pConst 26 WAITING_FOR_ELEMENT_TO_COME_BACK_MS = 500 27 HINT_WORKER_DELAY_MS = 2000 28 HINT_DISPLAY_DELAY_MS = 250 29 30 class UIPointerSvc(Service): 31 __exportedcalls__ = {} 32 __guid__ = 'svc.uipointerSvc' 33 __notifyevents__ = ['OnSessionChanged', 34 'ProcessSessionReset', 35 'OnEveMenuShown', 36 'DoBallRemove', 37 'OnUIScalingChange', 38 'OnToggleWindowed', 39 'DoBallsRemove', 40 'OnInvItemDropEnded'] 41 __servicename__ = 'UIPointers' 42 __displayname__ = 'UI Pointer Service' 43 __dependencies__ = ['michelle'] 44 specialCasedPointers = (pConst.UNIQUE_NAME_LOCAL_CHAT, 45 pConst.UNIQUE_NAME_CORP_CHAT, 46 pConst.UNIQUE_NAME_FLEET_CHAT, 47 pConst.UNIQUE_NAME_NEXT_IN_ROUTE) 48 49 def __init__(self): 50 Service.__init__(self) 51 self.running = True 52 self.pointersAreSuppressed = False 53 self.oldObscurers = {} 54 self.statelogger = StateLogger(self) 55 56 def Run(self, memStream = None): 57 self.elementUrls = collections.defaultdict(list) 58 self.pointerUIByItemID = {} 59 self.spaceObjectPointerLock = uthread.Semaphore() 60 self.pointerDataByItemID = {} 61 self.pointerDataByDungeonObjectID = {} 62 self.pointerDataByDungeonEntityGroupID = {} 63 self.pointerDataByTypeID = {} 64 self.pointerDataByByGroupID = {} 65 self.pointerDataNextInRoute = None 66 self.suppressedSpaceObjectUiPointers = set() 67 self.spaceObjectUiPointerUpdater = None 68 self.uiPointersByPointToID = {} 69 self.uiElementsToHighlight = set() 70 self.neocomSvc = sm.GetService('neocom') 71 self.uiPointerUpdateThread = None 72 on_unique_ui_element_created.connect(self._OnUniqueUiElementCreated) 73 on_unique_ui_element_destroyed.connect(self._OnUniqueUiElementDestroyed) 74 75 def Stop(self, memStream = None): 76 self.running = False 77 self.ClearPointers() 78 on_unique_ui_element_created.disconnect(self._OnUniqueUiElementCreated) 79 on_unique_ui_element_destroyed.disconnect(self._OnUniqueUiElementDestroyed) 80 81 def _OnUniqueUiElementCreated(self, uniqueName, element): 82 if uniqueName not in self.uiElementsToHighlight: 83 return 84 pointer = self.uiPointersByPointToID.get(uniqueName) 85 if pointer and (pointer.pointToElement is None or pointer.pointToElement.destroyed): 86 pointer.pointToElement = element 87 self.ShowPointer(uniqueName) 88 89 def _OnUniqueUiElementDestroyed(self, uniqueName): 90 if uniqueName in self.uiPointersByPointToID: 91 self.HidePointer(uniqueName) 92 93 def ProcessSessionReset(self): 94 self.ClearPointers() 95 96 def OnSessionChanged(self, isremote, sess, change): 97 if 'charid' in change and change['charid'][1] is not None: 98 self.Run() 99 if 'shipid' in change: 100 oldShipID, newShipID = change['shipid'] 101 if newShipID is not None: 102 self.FlushSpaceObjectPointer(newShipID) 103 104 def OnEveMenuShown(self): 105 for pointer in self.uiPointersByPointToID.values(): 106 if pointer.pointToElement.name == 'eveMenuBtn': 107 self.ClearPointers() 108 109 def OnUIScalingChange(self, change, *args): 110 for pointToID in self.uiElementsToHighlight.copy(): 111 self.RestartUiPointer(pointToID) 112 113 if self.spaceObjectUiPointerUpdater is not None: 114 self.RestartSpacePointers(clearPointers=True) 115 116 def OnToggleWindowed(self, isWindowed): 117 if not isWindowed: 118 self.RestartSpacePointers(clearPointers=True) 119 120 def ShouldHidePointers(self): 121 if uicore.layer.systemmenu.isopen: 122 return True 123 if sm.GetService('viewState').IsViewActive('charactercreation'): 124 return True 125 return self.pointersAreSuppressed 126 127 def SuppressPointers(self): 128 self.pointersAreSuppressed = True 129 130 def RevealPointers(self): 131 self.pointersAreSuppressed = False 132 133 def UpdatePointers(self): 134 if not self.uiElementsToHighlight: 135 self.uiPointerUpdateThread = None 136 for pointToID in self.uiElementsToHighlight.copy(): 137 try: 138 self._UpdatePointer(pointToID) 139 except: 140 log.LogException("UpdatePointers failed for pointToID '%s'." % str(pointToID)) 141 142 def _UpdatePointer(self, pointToID): 143 currentPointer = self.uiPointersByPointToID.get(pointToID, None) 144 if currentPointer is None: 145 return 146 if self.ShouldHidePointers(): 147 self.HidePointer(pointToID) 148 else: 149 uiPointerElement = currentPointer.uiPointerElement 150 pointToElement = currentPointer.pointToElement 151 considerations = currentPointer.considerations 152 oldPointLeft = currentPointer.oldPointLeft 153 oldPointUp = currentPointer.oldPointUp 154 oldPointDown = currentPointer.oldPointDown 155 basicData = currentPointer.basicData 156 if pointToElement is None or pointToElement.destroyed or isinstance(pointToElement, Window) and pointToElement.stacked: 157 blue.pyos.synchro.SleepWallclock(WAITING_FOR_ELEMENT_TO_COME_BACK_MS) 158 elementUrl = self.elementUrls.get(pointToID, None) 159 rediscoveredKeyVal = self.FindElementToPointTo(elementUrl) 160 rediscoveredElement = rediscoveredKeyVal.pointToElement if rediscoveredKeyVal else None 161 if rediscoveredElement is None or rediscoveredElement.destroyed: 162 self.HidePointer(pointToID) 163 return 164 pointToElement = rediscoveredElement 165 currentPointer.pointToElement = rediscoveredElement 166 uiPointerElement.UpdatePointToElement(rediscoveredElement) 167 locationData = self.GetLocation(basicData, pointToElement, considerations) 168 cumTop, cumLeft, pointLeft, pointUp, pointDown, isObscured, arrowPos = locationData 169 if cumTop <= 0 and cumLeft <= 0 or pointToElement.state == uiconst.UI_HIDDEN: 170 self.HidePointer(pointToID) 171 else: 172 self.ShowPointer(pointToID) 173 if uiPointerElement.destroyed or pointLeft != oldPointLeft or pointUp != oldPointUp or pointDown != oldPointDown: 174 self.ClearPointer(pointToID) 175 basicData.audioSetting = False 176 elementContainer = self.ConstructPointer(pointLeft, pointUp, pointDown, arrowPos, basicData, pointToElement) 177 elementContainer.SetPosition(cumLeft, cumTop) 178 self.uiPointersByPointToID[pointToID] = UiPointerData(uiPointerElement=elementContainer, pointToElement=pointToElement, considerations=considerations, oldPointLeft=pointLeft, oldPointUp=pointUp, oldPointDown=pointDown, basicData=basicData) 179 uthread.new(self._UpdatePointer, pointToID) 180 else: 181 uiPointerElement.SetPosition(cumLeft, cumTop) 182 183 def FindDeep(self, element, idOfItemToFind, shouldExcludeInvisible = False): 184 if getattr(element, 'uniqueUiName', None) == idOfItemToFind or getattr(element, 'name', None) == idOfItemToFind: 185 if not shouldExcludeInvisible or getattr(element, 'display', False): 186 return element 187 children = getattr(element, 'children', None) 188 if not children: 189 return 190 for child in children: 191 results = self.FindDeep(child, idOfItemToFind, shouldExcludeInvisible) 192 if results is not None: 193 return results 194 195 def GetLocation(self, basicData, element, directive): 196 pointToID = basicData.pointToID 197 defaultDirection = basicData.defaultDirection 198 offset = basicData.offset or 0 199 try: 200 parent = element 201 while hasattr(parent, 'parent'): 202 if parent is None or parent.state == uiconst.UI_HIDDEN: 203 return (-999, 204 -999, 205 False, 206 False, 207 False, 208 False, 209 0) 210 if self.SkipElementDueToClipping(element, parent) and not basicData.showEvenOffscreen: 211 return (-999, 212 -999, 213 False, 214 False, 215 False, 216 False, 217 0) 218 parent = parent.parent 219 220 if pointToID in self.uiPointersByPointToID: 221 uiPointer = self.uiPointersByPointToID[pointToID] 222 pointerWidth = uiPointer.uiPointerElement.width 223 pointerHeight = uiPointer.uiPointerElement.height 224 else: 225 uiPointer = None 226 pointerWidth, pointerHeight = UIPOINTER_WIDTH, UIPOINTER_HEIGHT 227 if directive == 'shipui' and element.parent.name == 'slotsContainer': 228 if element.parent.name == 'slotsContainer': 229 slotsContainer = element.parent 230 cumTop, cumLeft = slotsContainer.absoluteTop + element.top, slotsContainer.absoluteLeft + element.left + 5 231 elif directive == 'bracket': 232 parent = element.parent 233 cumTop, cumLeft = parent.absoluteTop + element.top, parent.absoluteLeft + element.left - 2 234 elif directive == 'neocom': 235 cumTop, cumLeft = element.absoluteTop, element.absoluteLeft 236 elif hasattr(element, 'absoluteTop') and hasattr(element, 'absoluteLeft'): 237 cumTop, cumLeft = element.absoluteTop - 2, element.absoluteLeft - 2 238 else: 239 cumTop, cumLeft = element.parent.absoluteTop + element.top - 2, element.parent.absoluteLeft + element.left - 2 240 pointLeft = True 241 height = element.height if element.height else element.absoluteBottom - element.absoluteTop 242 width = element.width if element.width else element.absoluteRight - element.absoluteLeft 243 cumTop += height / 2 - pointerHeight / 2 244 if defaultDirection in (UiHighlightDirections.UP, UiHighlightDirections.DOWN): 245 pointerLeft = cumLeft + width / 2 - pointerWidth / 2 246 else: 247 pointerLeft = cumLeft - offset - pointerWidth 248 if pointerLeft <= 0: 249 cumLeft = self._MoveToRightSide(element, directive, cumLeft) + offset 250 else: 251 pointLeft = False 252 cumLeft = self._MoveToLeftSide(cumLeft, pointerWidth) - offset 253 pointUp = False 254 pointDown = False 255 arrowPos = 0 256 if cumTop < 0 or defaultDirection == UiHighlightDirections.UP and cumTop - pointerHeight / 2 < 0: 257 if directive == 'neocom': 258 additionalSpace = 8 259 arrowPos = cumTop - additionalSpace 260 cumTop = additionalSpace 261 else: 262 pointUp = True 263 cumTop, cumLeft = self._MoveToBottom(element, directive, cumTop, height, pointerWidth, pointerHeight) 264 cumTop += offset 265 elif cumTop + pointerHeight > uicore.desktop.height: 266 if directive == 'neocom': 267 additionalSpace = 8 268 cumTop = uicore.desktop.height - pointerHeight - additionalSpace 269 else: 270 pointDown = True 271 cumTop, cumLeft = self._MoveToTop(pointToID, element, pointerWidth, pointerHeight) 272 cumTop -= offset 273 elif pointLeft == False: 274 if directive == 'bracket': 275 cumTop -= 3 276 elif directive == 'bracket': 277 cumLeft += POINTER_ARROW_WIDTH 278 cumTop -= 3 279 if directive == 'eveMenuBtn': 280 cumTop, cumLeft, pointLeft, pointUp, pointDown, arrowPos = self.GetEveMenuLocation(pointerHeight, pointerWidth) 281 if self._ShouldMoveToAuthoredSide(defaultDirection, pointLeft, pointUp, pointDown): 282 if defaultDirection == UiHighlightDirections.RIGHT: 283 newLeft = self._MoveToRightSide(element, directive, cumLeft) + pointerWidth 284 newLeft += 2 * offset 285 if self._IsLeftInView(newLeft, pointerWidth): 286 pointLeft = True 287 cumLeft = newLeft 288 elif defaultDirection == UiHighlightDirections.UP: 289 newTop, newLeft = self._MoveToTop(pointToID, element, pointerWidth, pointerHeight) 290 newTop -= offset 291 if self._IsInView(newLeft, newTop, pointerWidth, pointerHeight): 292 pointDown = True 293 cumLeft = newLeft 294 cumTop = newTop 295 elif defaultDirection == UiHighlightDirections.DOWN: 296 if uiPointer and uiPointer.uiPointerElement.pointDirections: 297 if not uiPointer.uiPointerElement.pointDirections[0]: 298 pointerHeight += POINTER_ARROW_HEIGHT 299 newTop, newLeft = self._MoveToBottom(element, directive, cumTop, height, pointerWidth, pointerHeight) 300 newTop += offset 301 if self._IsInView(newLeft, newTop, pointerWidth, pointerHeight): 302 pointUp = True 303 cumLeft = newLeft 304 cumTop = newTop 305 if basicData.showEvenOffscreen: 306 cumLeft = mathext.clamp(cumLeft, 0, uicore.desktop.width - width) 307 cumTop = mathext.clamp(cumTop, 0, uicore.desktop.height - height) 308 isObscured = self.CheckIsElementObscured(pointToID, cumTop, cumLeft, pointLeft, element) 309 return (cumTop, 310 cumLeft, 311 pointLeft, 312 pointUp, 313 pointDown, 314 isObscured, 315 arrowPos) 316 except: 317 log.LogException('GetLocation encountered an Exception.') 318 sys.exc_clear() 319 return (-999, 320 -999, 321 False, 322 False, 323 False, 324 False, 325 0) 326 327 def SkipElementDueToClipping(self, element, parent): 328 if getattr(element, pConst.ALLOWS_POINTER_CLIPPING, False): 329 return False 330 clipped = element.IsCompletelyClipped(parent) 331 return clipped 332 333 def _IsInView(self, left, top, pointerWidth, pointerHeight): 334 return self._IsLeftInView(left, pointerWidth) and self._IsTopInView(top, pointerHeight) 335 336 def _IsLeftInView(self, left, pointerWidth): 337 return left + pointerWidth < uicore.desktop.width 338 339 def _IsTopInView(self, top, pointerHeight): 340 return top + pointerHeight < uicore.desktop.height 341 342 def _ShouldMoveToAuthoredSide(self, defaultDirection, pointLeft, pointUp, pointDown): 343 pointRight = not (pointLeft or pointUp or pointDown) 344 return defaultDirection and defaultDirection != UiHighlightDirections.LEFT and pointRight 345 346 def _MoveToRightSide(self, element, directive, cumLeft): 347 width = self._GetElementWidth(element, directive) 348 return cumLeft + width + 2 349 350 def _MoveToLeftSide(self, cumLeft, pointerWidth): 351 return cumLeft - pointerWidth + 2 352 353 def _MoveToTop(self, pointToID, element, pointerWidth, pointerHeight): 354 pointer = self.uiPointersByPointToID.get(pointToID, None) 355 if pointer is None: 356 currentPointerHeight = 0 357 else: 358 uiPointerElement = pointer.uiPointerElement 359 currentPointerHeight = uiPointerElement.height 360 if currentPointerHeight < 1: 361 currentPointerHeight = pointerHeight 362 newTop = element.absoluteTop - currentPointerHeight 363 newLeft = element.absoluteLeft - pointerWidth / 2 + (element.absoluteRight - element.absoluteLeft) / 2 364 if newLeft < 0: 365 newLeft = 0 366 elif newLeft + pointerWidth > uicore.desktop.width: 367 newLeft = uicore.desktop.width - pointerWidth 368 return (newTop, newLeft) 369 370 def _MoveToBottom(self, element, directive, cumTop, height, pointerWidth, pointerHeight): 371 newTop = cumTop + height / 2 + pointerHeight / 2 372 if hasattr(element, 'absoluteLeft') and hasattr(element, 'absoluteRight'): 373 newLeft = element.absoluteLeft - pointerWidth / 2 + (element.absoluteRight - element.absoluteLeft) / 2 374 else: 375 newLeft = element.parent.absoluteLeft + element.left - pointerWidth / 2 376 if newLeft < 0: 377 newLeft = 0 378 elif newLeft + pointerWidth > uicore.desktop.width: 379 newLeft = uicore.desktop.width - pointerWidth 380 if directive == 'bracket': 381 newTop -= 8 382 return (newTop, newLeft) 383 384 def _GetElementWidth(self, element, directive): 385 if directive == 'neocom': 386 neocom = sm.GetService('neocom').GetNeocomContainer() 387 if neocom is not None: 388 width = neocom.width 389 else: 390 width = element.width 391 if width == 0: 392 width = element.absoluteRight - element.absoluteLeft 393 else: 394 width = element.width 395 if width < 1: 396 width = element.absoluteRight - element.absoluteLeft 397 return width 398 399 def GetEveMenuLocation(self, pointerHeight, pointerWidth): 400 neocomIsLeftAligned = neocomSettings.neocom_align_setting.get() == uiconst.TOLEFT 401 pointLeft = neocomIsLeftAligned 402 pointUp = False 403 pointDown = False 404 eveMenuWidth, eveMenuHeight = self.neocomSvc.GetEveMenuButtonSize() 405 cumTop = 0 406 cumLeft = eveMenuWidth if neocomIsLeftAligned else uicore.desktop.width - eveMenuWidth - pointerWidth 407 arrowPos = (eveMenuHeight - pointerHeight) / 2 if eveMenuHeight else 0 408 return (cumTop, 409 cumLeft, 410 pointLeft, 411 pointUp, 412 pointDown, 413 arrowPos) 414 415 def CheckIsElementObscured(self, pointToID, top, left, pointLeft, element): 416 globalLayer = uicore.layer.main 417 abovemain = uicore.layer.abovemain 418 candidates = self.GetObscureCandidates(globalLayer, element, False) 419 candidates.extend(self.GetObscureCandidates(abovemain, element, True)) 420 left, top, width, height = element.GetAbsolute() 421 elementPoints = [] 422 elementPoints.append(utillib.KeyVal(x=left, y=top)) 423 elementPoints.append(utillib.KeyVal(x=left, y=top + height)) 424 elementPoints.append(utillib.KeyVal(x=left + width, y=top)) 425 elementPoints.append(utillib.KeyVal(x=left + width, y=top + height)) 426 occluded = False 427 occludors = [] 428 for candidate in candidates: 429 absLeft, absTop, width, height = candidate.GetAbsolute() 430 absRight = absLeft + width 431 absBottom = absTop + height 432 for point in elementPoints: 433 if point.x > absLeft and point.x < absRight and point.y > absTop and point.y < absBottom: 434 occluded = True 435 occludors.append(candidate) 436 break 437 438 self.UpdateObscurers(pointToID, occludors) 439 return occluded 440 441 def GetObscureCandidates(self, layer, pointToElement, topLayer): 442 parentWindow = self.GetElementsParent(pointToElement) 443 parentIdx = self.GetElementIdx(parentWindow) 444 if parentIdx is None: 445 return [] 446 list = [] 447 for window in layer.children: 448 windowIdx = self.GetElementIdx(window) 449 if windowIdx is None: 450 return [] 451 if hasattr(window, 'name') and window.name not in ('UIPointer', 452 'locationInfo', 453 'snapIndicator', 454 'windowhilite', 455 parentWindow.name) and hasattr(window, 'state') and window.state != uiconst.UI_HIDDEN and hasattr(window, 'absoluteTop') and hasattr(window, 'absoluteBottom') and hasattr(window, 'absoluteRight') and hasattr(window, 'absoluteLeft'): 456 if not topLayer and windowIdx < parentIdx: 457 list.append(window) 458 elif topLayer: 459 list.append(window) 460 461 return list 462 463 def UpdateObscurers(self, pointToID, obscurers): 464 oldObscurers = self.oldObscurers.get(pointToID, []) 465 for window in oldObscurers: 466 if window not in obscurers: 467 window.opacity = 1.0 468 469 for window in obscurers: 470 window.opacity = 0.6 471 472 self.oldObscurers[pointToID] = obscurers 473 474 def ClearObscurers(self, pointToID): 475 oldObscurers = self.oldObscurers.pop(pointToID, []) 476 for window in oldObscurers: 477 window.opacity = 1.0 478 479 def GetElementIdx(self, element): 480 if element.name == 'aura9': 481 return 0 482 parent = element.parent 483 if not parent: 484 return None 485 elementIndex = 0 486 for child in parent.children: 487 if child == element: 488 break 489 elementIndex += 1 490 491 return elementIndex 492 493 def GetElementsParent(self, element): 494 parentWindow = element 495 while True: 496 if not hasattr(parentWindow.parent, 'parent'): 497 break 498 elif not hasattr(parentWindow.parent.parent, 'parent'): 499 break 500 elif not hasattr(parentWindow.parent.parent.parent, 'parent'): 501 break 502 else: 503 parentWindow = parentWindow.parent 504 505 return parentWindow 506 507 def FindElementToPointTo(self, elementUrl, shouldExcludeInvisible = False, blinkNeocom = True, findWindow = False): 508 elementKeyVal = utillib.KeyVal(pointToElement=None) 509 if not elementUrl: 510 return elementKeyVal 511 if isinstance(elementUrl, basestring): 512 elementUrl = elementUrl.split('.') 513 if len(elementUrl) == 1: 514 elementName = elementUrl[0] 515 if elementName in self.specialCasedPointers: 516 return self.FindSpecialCasedElements(elementName) 517 pointToElement = self.FindDeep(uicore.desktop, elementName, shouldExcludeInvisible) 518 elif len(elementUrl) == 2 and elementUrl[0] == 'neocom': 519 wndID = elementUrl[1] 520 pointToElement = self._FindNeocomeElementToPointTo(elementUrl, findWindow) 521 neocomSvc = sm.GetService('neocom') 522 if not neocomSvc.IsButtonVisible(wndID) and blinkNeocom: 523 neocomSvc.Blink(wndID) 524 else: 525 parent = uicore.desktop 526 for path in elementUrl[:-1]: 527 parent = self.FindDeep(parent, path, shouldExcludeInvisible) 528 529 pointToElement = self.FindDeep(parent, elementUrl[-1], shouldExcludeInvisible) 530 if isinstance(pointToElement, Window) and pointToElement.stacked: 531 pointToElement = pointToElement.sr.tab 532 elementKeyVal.pointToElement = pointToElement 533 return elementKeyVal 534 535 def _FindNeocomeElementToPointTo(self, elementUrl, findWindow = False): 536 wndID = elementUrl[1] 537 neocomSvc = sm.GetService('neocom') 538 pointToElement, node = neocomSvc.GetUIObjectAndNodeByID(wndID) 539 if findWindow and node: 540 newPointToElement = self._FindPointToElmentFromNode(node) 541 if newPointToElement: 542 pointToElement = newPointToElement 543 if pointToElement is None: 544 pointToElement = neocomSvc.FindParentGroupBtn(wndID) 545 if pointToElement is None: 546 uniqueUiName = '.'.join(elementUrl) 547 pointToElement = neocomSvc.GetUIObjectByUniqueUiName(uniqueUiName) 548 if pointToElement: 549 btnData = getattr(pointToElement, 'btnData', None) 550 if neocomSvc.IsBtnInOverflowList(btnData): 551 pointToElement = neocomSvc.GetNeocomContainer().overflowBtn 552 return pointToElement 553 554 def _FindPointToElmentFromNode(self, node): 555 wndClass = getattr(node, 'wndCls', None) 556 if not wndClass: 557 return 558 wnd = wndClass.GetIfOpen() 559 if wnd and not wnd.destroyed and not wnd.IsMinimized() and wnd.IsVisible(): 560 if wnd.stacked: 561 return wnd.stack 562 else: 563 return wnd 564 565 def PointTo(self, basicData): 566 self.LogInfo('PointTo', basicData.pointToID, basicData.uiPointerText, basicData.uiPointerTitle) 567 if basicData.pointToID is None or basicData.pointToID == '': 568 return 569 self.uiElementsToHighlight.add(basicData.pointToID) 570 if self.uiPointerUpdateThread is None: 571 self.uiPointerUpdateThread = AutoTimer(20, self.UpdatePointers) 572 uthread.new(self._PointToThread, basicData) 573 sm.ScatterEvent('OnUiHighlightCreated', basicData.pointToID) 574 575 def _PointToThread(self, basicData): 576 while basicData.pointToID in self.uiElementsToHighlight and basicData.pointToID not in self.uiPointersByPointToID: 577 self._PointToElementByID(basicData) 578 blue.pyos.synchro.SleepWallclock(20) 579 580 def StopPointingTo(self, pointToID): 581 if pointToID not in self.uiElementsToHighlight: 582 return 583 self.uiElementsToHighlight.remove(pointToID) 584 self.ClearPointer(pointToID) 585 self.elementUrls.pop(pointToID, None) 586 data = self.uiPointersByPointToID.pop(pointToID, None) 587 if data and data.basicData.highlightContent: 588 data.basicData.highlightContent.Close() 589 sm.ScatterEvent('OnUiHighlightDeleted', pointToID) 590 591 def IsAnyPointerActive(self): 592 return bool(self.uiElementsToHighlight) 593 594 def _GetConsiderationsForPointToElement(self, pointToID, pointToElement): 595 considerations = None 596 elementUrl = self.elementUrls.get(pointToID, None) 597 if elementUrl and elementUrl[0] in ('neocom', pConst.UNIQUE_NAME_CLOCK_CALENDAR): 598 considerations = 'neocom' 599 elif elementUrl and elementUrl[0].replace(pConst.UNIQUE_UI_PREFIX, '') == 'eveMenuBtn': 600 considerations = 'eveMenuBtn' 601 parent = pointToElement 602 while considerations is None and getattr(parent, 'parent', None) and hasattr(parent.parent, 'name'): 603 parent = parent.parent 604 if parent.name == 'shipui': 605 considerations = 'shipui' 606 elif parent.name == 'l_bracket': 607 considerations = 'bracket' 608 609 return considerations 610 611 def _PointToElementByID(self, basicData): 612 pointToID = basicData.pointToID 613 if pointToID in self.uiPointersByPointToID: 614 return 615 self.elementUrls[pointToID] = pointToID.split('.') 616 if not self.ShouldHidePointers(): 617 elementUrl = self.elementUrls.get(pointToID, None) 618 pointToElement = None 619 if basicData.shouldPrioritizeVisible: 620 elementKeyVal = self.FindElementToPointTo(elementUrl, shouldExcludeInvisible=True) 621 pointToElement = elementKeyVal.pointToElement if elementKeyVal else None 622 if not pointToElement: 623 elementKeyVal = self.FindElementToPointTo(elementUrl) 624 pointToElement = elementKeyVal.pointToElement if elementKeyVal else None 625 if pointToElement is not None and pointToElement.state != uiconst.UI_HIDDEN: 626 considerations = self._GetConsiderationsForPointToElement(pointToID, pointToElement) 627 locationData = self.GetLocation(basicData, pointToElement, considerations) 628 cumTop, cumLeft, pointLeft, pointUp, pointDown, isObscured, arrowPos = locationData 629 elementContainer = self.ConstructPointer(pointLeft, pointUp, pointDown, arrowPos, basicData, pointToElement) 630 self.uiPointersByPointToID[pointToID] = UiPointerData(uiPointerElement=elementContainer, pointToElement=pointToElement, considerations=considerations, oldPointLeft=pointLeft, oldPointUp=pointUp, oldPointDown=pointDown, basicData=basicData) 631 self._UpdatePointer(pointToID) 632 self.statelogger.log_visible(pointToID) 633 elif pointToElement is None: 634 self.statelogger.log_element_not_found(pointToID) 635 else: 636 self.statelogger.log_element_invisible(pointToID) 637 638 def ClearPointer(self, pointToID): 639 pointer = self.uiPointersByPointToID.get(pointToID, None) 640 if pointer is not None: 641 element = pointer.uiPointerElement 642 element.Close() 643 if element in uicore.layer.hint.children: 644 uicore.layer.hint.children.remove(element) 645 self.ClearObscurers(pointToID) 646 self.statelogger.clear_pointer(pointToID) 647 648 def ClearPointers(self): 649 for pointToID in self.uiElementsToHighlight.copy(): 650 self.StopPointingTo(pointToID) 651 652 self.uiPointersByPointToID.clear() 653 654 def RestartUiPointer(self, pointToID): 655 pointer = self.uiPointersByPointToID.get(pointToID, None) 656 if not pointer: 657 return 658 basicData = pointer.basicData 659 self.StopPointingTo(pointToID) 660 self.PointTo(basicData) 661 662 def GetUiPointerClass(self, basicData): 663 if basicData.highlightContent: 664 return CustomUiPointer 665 uiClass = basicData.uiClass 666 if uiClass in UI_CLASS_BY_NAME: 667 return UI_CLASS_BY_NAME[uiClass] 668 return DEFAULT_UI_CLASS 669 670 def ConstructPointer(self, pointLeft, pointUp, pointDown, arrowPosition, basicData, pointToElement): 671 layer = uicore.layer.hint 672 if basicData.audioSetting: 673 uihighlightaudio.manager.play_highlight_appeared_sound() 674 rectTop = 128 675 if pointLeft: 676 rectTop = 0 677 defaultWidth = UIPOINTER_WIDTH 678 defaultHeight = UIPOINTER_HEIGHT 679 if pointUp or pointDown: 680 defaultHeight -= POINTER_ARROW_WIDTH 681 else: 682 defaultWidth -= POINTER_ARROW_HEIGHT 683 uiPointerClass = self.GetUiPointerClass(basicData) 684 idx = basicData.idx if basicData.idx is not None else -1 685 uiPointer = uiPointerClass(parent=layer, name='uiPointer', idx=idx, width=defaultWidth, height=defaultHeight, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, rectTop=rectTop, pointDirections=(pointUp, pointDown, pointLeft), arrowPositionModifier=arrowPosition, useArrowPointer=True, basicData=basicData, highlightContent=basicData.highlightContent, pointToElement=pointToElement) 686 uiPointer.ConstructContent() 687 return uiPointer 688 689 def HidePointer(self, pointToID): 690 pointer = self.uiPointersByPointToID.get(pointToID, None) 691 if pointer is not None: 692 self.statelogger.log_hidden(pointToID) 693 pointer.uiPointerElement.Hide() 694 695 def ShowPointer(self, pointToID): 696 pointer = self.uiPointersByPointToID.get(pointToID, None) 697 if pointer is not None: 698 self.statelogger.log_visible(pointToID) 699 pointer.uiPointerElement.Show() 700 701 def _AddSpaceObjectTypeUiPointer(self, typeID, groupID, message, hint, audioSetting, localize = True, itemID = None, objectID = None, entityGroupID = None, showBox = True, highlightBracket = True): 702 self.LogInfo('AddSpaceObjectTypeUiPointer', typeID, groupID, message, hint, localize, itemID, objectID) 703 with self.spaceObjectPointerLock: 704 if itemID is not None and itemID not in self.pointerUIByItemID: 705 self.pointerDataByItemID[itemID] = SpaceObjectUiPointerData(None, None, itemID, message, hint, audioSetting, localize, showBox, highlightBracket) 706 elif objectID is not None and objectID not in self.pointerDataByDungeonObjectID: 707 self.pointerDataByDungeonObjectID[objectID] = SpaceObjectUiPointerData(None, None, None, message, hint, audioSetting, localize, showBox, highlightBracket) 708 elif entityGroupID is not None and entityGroupID not in self.pointerDataByDungeonEntityGroupID: 709 self.pointerDataByDungeonEntityGroupID[entityGroupID] = SpaceObjectUiPointerData(None, None, None, message, hint, audioSetting, localize, showBox, highlightBracket) 710 elif typeID is not None and typeID not in self.pointerDataByTypeID: 711 self.pointerDataByTypeID[typeID] = SpaceObjectUiPointerData(typeID, None, None, message, hint, audioSetting, localize, showBox, highlightBracket) 712 elif groupID is not None and groupID not in self.pointerDataByByGroupID: 713 self.pointerDataByByGroupID[groupID] = SpaceObjectUiPointerData(None, groupID, None, message, hint, audioSetting, localize, showBox, highlightBracket) 714 self.RestartSpacePointers(clearPointers=False) 715 716 def AddSpaceObjectTypeUiPointer(self, typeID, groupID, message, hint, audioSetting, localize = True, itemID = None, objectID = None, entityGroupID = None, showBox = True, highlightBracket = True): 717 uthread.new(self._AddSpaceObjectTypeUiPointer, typeID, groupID, message, hint, audioSetting, localize=localize, itemID=itemID, objectID=objectID, entityGroupID=entityGroupID, showBox=showBox, highlightBracket=highlightBracket) 718 719 def AddNextInRouteUiPointer(self, message, hint, audioSetting, showBox = True, highlightBracket = True): 720 self.pointerDataNextInRoute = SpaceObjectUiPointerData(None, None, message, hint, audioSetting, False, showBox, highlightBracket) 721 self.RestartSpacePointers(clearPointers=False) 722 723 def RestartSpacePointers(self, clearPointers = False): 724 self.KillSpacePointerUpdater() 725 if clearPointers: 726 for itemID in self.pointerUIByItemID.keys(): 727 self.FlushSpaceObjectPointer(itemID) 728 729 self.StartPointerUpdater() 730 731 def StartPointerUpdater(self): 732 if self.spaceObjectUiPointerUpdater is None and any([bool(self.pointerDataByItemID), 733 bool(self.pointerDataByTypeID), 734 bool(self.pointerDataByByGroupID), 735 bool(self.pointerDataByDungeonObjectID), 736 bool(self.pointerDataByDungeonEntityGroupID)]): 737 self.spaceObjectUiPointerUpdater = uthread.new(self.UpdateSpaceObjectUiPointersThread) 738 739 def KillSpacePointerUpdater(self): 740 if self.spaceObjectUiPointerUpdater is not None: 741 self.spaceObjectUiPointerUpdater.kill() 742 self.spaceObjectUiPointerUpdater = None 743 744 def UpdateSpaceObjectUiPointersThread(self): 745 blue.pyos.synchro.SleepWallclock(HINT_DISPLAY_DELAY_MS) 746 while self.spaceObjectUiPointerUpdater is not None: 747 if not self._UpdateSpaceObjectUiPointers(): 748 return 749 blue.pyos.synchro.SleepWallclock(HINT_WORKER_DELAY_MS) 750 751 def _UpdateSpaceObjectUiPointers(self): 752 bp = self.michelle.GetBallpark() 753 if bp is None: 754 return False 755 with self.spaceObjectPointerLock: 756 self.CheckAddOrRemovePointers(bp) 757 return True 758 759 def CheckAddOrRemovePointers(self, bp): 760 for slimItem in bp.slimItems.values(): 761 if slimItem.charID: 762 continue 763 elif slimItem.itemID in self.suppressedSpaceObjectUiPointers: 764 continue 765 data = self._GetPointerData(slimItem) 766 if data is not None: 767 if slimItem.itemID not in self.pointerUIByItemID: 768 self.pointerUIByItemID[slimItem.itemID] = SpaceObjectPointer(slimItem, data) 769 self.LogInfo('Creating UI Pointer for item', slimItem.itemID, 'of type', slimItem.typeID) 770 sm.ScatterEvent('OnItemHighlighted', slimItem.itemID, True) 771 elif slimItem.itemID in self.pointerUIByItemID: 772 self.FlushSpaceObjectPointer(slimItem.itemID) 773 774 def _GetPointerData(self, slimItem): 775 dunObjectID = getattr(slimItem, 'dunObjectID', None) 776 dunObjectNameID = getattr(slimItem, 'dunObjectNameID', None) 777 dunEntityGroupID = getattr(slimItem, 'entityGroupID', None) 778 data = self.pointerDataByItemID.get(slimItem.itemID, None) 779 if data is None and dunObjectID: 780 data = self.pointerDataByDungeonObjectID.get(dunObjectID) 781 if data is None: 782 data = self.pointerDataByDungeonEntityGroupID.get(dunEntityGroupID) 783 if data is None: 784 data = self.pointerDataByTypeID.get(slimItem.typeID) 785 if data is None: 786 data = self.pointerDataByByGroupID.get(slimItem.groupID) 787 if data is None and self.pointerDataNextInRoute and slimItem.itemID == sm.GetService('autoPilot').GetNextItemIDInRoute(): 788 data = self.pointerDataNextInRoute 789 return data 790 791 def _RemoveSpaceObjectUiPointers(self): 792 self.KillSpacePointerUpdater() 793 with self.spaceObjectPointerLock: 794 self.pointerDataByItemID.clear() 795 self.pointerDataByDungeonObjectID.clear() 796 self.pointerDataByDungeonEntityGroupID.clear() 797 self.pointerDataByTypeID.clear() 798 self.pointerDataByByGroupID.clear() 799 self.pointerDataNextInRoute = None 800 self.suppressedSpaceObjectUiPointers.clear() 801 for pointerID in self.pointerUIByItemID.keys(): 802 self.FlushSpaceObjectPointer(pointerID) 803 804 self.LogInfo('Removing all space object pointers') 805 806 def RemoveSpaceObjectUiPointers(self): 807 uthread.new(self._RemoveSpaceObjectUiPointers) 808 809 def _RemoveSpaceObjectUiPointersByAttribute(self, attributeName, attributeValue): 810 self.KillSpacePointerUpdater() 811 pointersByAttribute = {'itemID': self.pointerDataByItemID, 812 'typeID': self.pointerDataByTypeID, 813 'groupID': self.pointerDataByByGroupID, 814 'dunObjectID': self.pointerDataByDungeonObjectID, 815 'dunEntityGroupID': self.pointerDataByDungeonEntityGroupID} 816 with self.spaceObjectPointerLock: 817 allAttributePointers = pointersByAttribute[attributeName] 818 pointersToRemove = [] 819 for pointer in allAttributePointers.values(): 820 attributeValueInPointer = getattr(pointer, attributeName, None) 821 if attributeValueInPointer is not None and attributeValueInPointer == attributeValue: 822 pointersToRemove.append(pointer) 823 824 if attributeValue in allAttributePointers: 825 del allAttributePointers[attributeValue] 826 for pointer in pointersToRemove: 827 itemID = pointer.itemID 828 self.FlushSpaceObjectPointer(itemID) 829 if itemID in self.suppressedSpaceObjectUiPointers: 830 self.suppressedSpaceObjectUiPointers.remove(itemID) 831 832 self._UpdateSpaceObjectUiPointers() 833 self.StartPointerUpdater() 834 835 def RemoveSpaceObjectUiPointersByItem(self, itemID): 836 uthread.new(self._RemoveSpaceObjectUiPointersByAttribute, 'itemID', itemID) 837 838 def RemoveSpaceObjectUiPointersByDungeonObject(self, objectID): 839 uthread.new(self._RemoveSpaceObjectUiPointersByAttribute, 'dunObjectID', objectID) 840 841 def RemoveSpaceObjectUiPointersByDungeonEntityGroup(self, entityGroupID): 842 uthread.new(self._RemoveSpaceObjectUiPointersByAttribute, 'dunEntityGroupID', entityGroupID) 843 844 def RemoveSpaceObjectUiPointersByType(self, typeID): 845 uthread.new(self._RemoveSpaceObjectUiPointersByAttribute, 'typeID', typeID) 846 847 def RemoveSpaceObjectUiPointersByGroup(self, groupID): 848 uthread.new(self._RemoveSpaceObjectUiPointersByAttribute, 'groupID', groupID) 849 850 def FlushSpaceObjectPointer(self, itemID): 851 pointer = self.pointerUIByItemID.pop(itemID, None) 852 if pointer is not None: 853 pointer.Close() 854 sm.ScatterEvent('OnItemHighlighted', itemID, False) 855 856 def SuppressSpaceObjectPointer(self, itemID): 857 self.suppressedSpaceObjectUiPointers.add(itemID) 858 self.LogInfo('space object pointer for', itemID, 'was suppressed') 859 self.FlushSpaceObjectPointer(itemID) 860 861 @telemetry.ZONE_METHOD 862 def DoBallsRemove(self, pythonBalls, isRelease): 863 for ball, slimItem, terminal in pythonBalls: 864 self.DoBallRemove(ball, slimItem, terminal) 865 866 def DoBallRemove(self, ball, slimItem, terminal): 867 self.FlushSpaceObjectPointer(slimItem.itemID) 868 869 def OnInvItemDropEnded(self, name): 870 if name: 871 self.RestartUiPointer(name) 872 873 def FindSpecialCasedElements(self, elementName): 874 if elementName == pConst.UNIQUE_NAME_NEXT_IN_ROUTE: 875 from eve.client.script.ui.inflight.overview.overviewWindowUtil import GetOverviewWndIfOpen 876 overview = GetOverviewWndIfOpen() 877 if not overview: 878 return 879 panel, direction = overview.GetNextInRouteNodeAndDirection() 880 textList = None 881 if direction == 'up': 882 textList = [GetByLabel('UI/Overview/HelpPointerScrollUp')] 883 elif direction == 'down': 884 textList = [GetByLabel('UI/Overview/HelpPointerScrollDown')] 885 return utillib.KeyVal(pointToElement=panel, extraTextLines=textList) 886 helpPointer = sm.GetService('helpPointer') 887 if elementName == pConst.UNIQUE_NAME_LOCAL_CHAT: 888 return utillib.KeyVal(pointToElement=helpPointer.FindLocalChatWindow(elementName)) 889 if elementName == pConst.UNIQUE_NAME_CORP_CHAT: 890 return utillib.KeyVal(pointToElement=helpPointer.FindCorpChatWindow(elementName)) 891 if elementName == pConst.UNIQUE_NAME_FLEET_CHAT: 892 return utillib.KeyVal(pointToElement=helpPointer.FindFleetChatWindow(elementName)) 893 return utillib.KeyVal(pointToElement=None)