pointerPanel.py (21365B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\control\pointerPanel.py 2 from carbonui.primitives.container import Container 3 from carbonui.primitives.frame import Frame 4 from carbonui.primitives.transform import Transform 5 from carbonui.primitives.layoutGrid import LayoutGrid 6 import carbonui.const as uiconst 7 import blue 8 import weakref 9 from eve.client.script.ui.control.themeColored import FrameThemeColored, SpriteThemeColored 10 from carbonui.uicore import uicore 11 LEFTGROUP = [uiconst.POINT_LEFT_2, uiconst.POINT_LEFT_1, uiconst.POINT_LEFT_3] 12 RIGHTGROUP = [uiconst.POINT_RIGHT_2, uiconst.POINT_RIGHT_1, uiconst.POINT_RIGHT_3] 13 TOPGROUP = [uiconst.POINT_TOP_2, uiconst.POINT_TOP_1, uiconst.POINT_TOP_3] 14 BOTTOMGROUP = [uiconst.POINT_BOTTOM_2, uiconst.POINT_BOTTOM_1, uiconst.POINT_BOTTOM_3] 15 CORNERGROUP = [uiconst.POINT_TOPLEFT, 16 uiconst.POINT_TOPRIGHT, 17 uiconst.POINT_BOTTOMLEFT, 18 uiconst.POINT_BOTTOMRIGHT] 19 POINT_CORRECTION = {uiconst.POINT_LEFT_1: LEFTGROUP + RIGHTGROUP + BOTTOMGROUP + TOPGROUP, 20 uiconst.POINT_LEFT_2: LEFTGROUP + RIGHTGROUP + BOTTOMGROUP + TOPGROUP, 21 uiconst.POINT_LEFT_3: LEFTGROUP + RIGHTGROUP + BOTTOMGROUP + TOPGROUP, 22 uiconst.POINT_RIGHT_1: RIGHTGROUP + LEFTGROUP + BOTTOMGROUP + TOPGROUP, 23 uiconst.POINT_RIGHT_2: RIGHTGROUP + LEFTGROUP + BOTTOMGROUP + TOPGROUP, 24 uiconst.POINT_RIGHT_3: RIGHTGROUP + LEFTGROUP + BOTTOMGROUP + TOPGROUP, 25 uiconst.POINT_BOTTOM_1: BOTTOMGROUP + LEFTGROUP + TOPGROUP + RIGHTGROUP, 26 uiconst.POINT_BOTTOM_2: [uiconst.POINT_LEFT_2] + BOTTOMGROUP + LEFTGROUP + TOPGROUP + RIGHTGROUP, 27 uiconst.POINT_BOTTOM_3: BOTTOMGROUP + LEFTGROUP + TOPGROUP + RIGHTGROUP, 28 uiconst.POINT_TOP_1: TOPGROUP + LEFTGROUP + BOTTOMGROUP + RIGHTGROUP, 29 uiconst.POINT_TOP_2: TOPGROUP + LEFTGROUP + BOTTOMGROUP + RIGHTGROUP, 30 uiconst.POINT_TOP_3: TOPGROUP + LEFTGROUP + BOTTOMGROUP + RIGHTGROUP} 31 FRAME_WITH_POINTER_SKIN_GENERAL = 'general' 32 FRAME_WITH_POINTER_SKIN_BADGE = 'badgeStyle' 33 34 class GeneralFrameWithPointerSkin(object): 35 36 def __init__(self): 37 self.skinName = 'general' 38 self.leftTexture = 'res:/UI/Texture/classes/FrameWithPointer/pointer_left_02.png' 39 self.rightTexture = 'res:/UI/Texture/classes/FrameWithPointer/pointer_right_02.png' 40 self.topRightTexture = 'res:/UI/Texture/classes/FrameWithPointer/pointer_topright_02.png' 41 self.topLeftTexture = 'res:/UI/Texture/classes/FrameWithPointer/pointer_topleft_02.png' 42 self.upTexture = 'res:/UI/Texture/classes/FrameWithPointer/pointer_up_02.png' 43 self.downTexture = 'res:/UI/Texture/classes/FrameWithPointer/pointer_down_02.png' 44 self.bottomLeftTexture = 'res:/UI/Texture/classes/FrameWithPointer/pointer_bottomleft_02.png' 45 self.bottomRightTexture = 'res:/UI/Texture/classes/FrameWithPointer/pointer_bottomright_02.png' 46 self.backgroundTexture = 'res:/UI/Texture/classes/FrameWithPointer/background_04.png' 47 self.backgroundOffset = -15 48 self.backgroundCornerSize = 19 49 50 51 class BadgeStyleFrameWithPointerSkin(GeneralFrameWithPointerSkin): 52 53 def __init__(self): 54 super(BadgeStyleFrameWithPointerSkin, self).__init__() 55 self.skinName = 'badgeStyle' 56 self.leftTexture = 'res:/UI/Texture/classes/Notifications/pointer_left_02.png' 57 self.rightTexture = 'res:/UI/Texture/classes/Notifications/pointer_right_02.png' 58 self.backgroundTexture = 'res:/UI/Texture/classes/Notifications/newItemsBadgeBase.png' 59 self.backgroundOffset = -6 60 self.backgroundCornerSize = 10 61 62 63 SKIN_NAME_TO_CLASS = {FRAME_WITH_POINTER_SKIN_GENERAL: GeneralFrameWithPointerSkin, 64 FRAME_WITH_POINTER_SKIN_BADGE: BadgeStyleFrameWithPointerSkin} 65 66 class PointerPanel(LayoutGrid): 67 default_state = uiconst.UI_PICKCHILDREN 68 default_align = uiconst.ABSOLUTE 69 default_opacity = 0.0 70 default_cellClipChildren = False 71 beingDestroyed = False 72 _owner = None 73 pointerSize = 9 74 scaleTransform = None 75 default_pointerDirection = uiconst.POINT_BOTTOM_2 76 77 def ApplyAttributes(self, attributes): 78 attributes.align = uiconst.TOPLEFT 79 LayoutGrid.ApplyAttributes(self, attributes) 80 self.pointerDirection = attributes.get('pointerDirection', self.default_pointerDirection) or self.default_pointerDirection 81 self.backgroundFrame = FrameWithPointer(bgParent=self, color=attributes.color) 82 if attributes.owner: 83 self.owner = attributes.owner 84 else: 85 self.opacity = 1.0 86 87 def SetBackgroundColor(self, color): 88 self.backgroundFrame.SetColor(color) 89 90 def SetBackgroundAlpha(self, alphaValue): 91 self.backgroundFrame.SetAlpha(alphaValue) 92 93 @apply 94 def owner(): 95 doc = "Weakref'd Owner of the panel" 96 97 def fset(self, value): 98 self._owner = weakref.ref(value) 99 100 def fget(self): 101 if self._owner: 102 owner = self._owner() 103 if owner and not owner.destroyed: 104 return owner 105 self._owner = None 106 107 return property(**locals()) 108 109 def Close(self, *args): 110 LayoutGrid.Close(self, *args) 111 if getattr(self, 'debugFrame', None): 112 self.debugFrame.Close() 113 if self.scaleTransform: 114 self.scaleTransform.Close() 115 self.scaleTransform = None 116 117 @apply 118 def pos(): 119 doc = 'Position of UI element' 120 121 def fget(self): 122 return (self._left, 123 self._top, 124 self._width, 125 self._height) 126 127 def fset(self, value): 128 left, top, width, height = value 129 adjustedLeft = int(round(left)) 130 adjustedTop = int(round(top)) 131 adjustedWidth = int(round(width)) 132 adjustedHeight = int(round(height)) 133 self._left = adjustedLeft 134 self._top = adjustedTop 135 self._width = adjustedWidth 136 self._height = adjustedHeight 137 self._displayX = uicore.ScaleDpi(adjustedLeft) 138 self._displayY = uicore.ScaleDpi(adjustedTop) 139 self._displayWidth = uicore.ScaleDpi(adjustedWidth) 140 self._displayHeight = uicore.ScaleDpi(adjustedHeight) 141 ro = self.renderObject 142 if ro: 143 ro.displayX = self._displayX 144 ro.displayY = self._displayY 145 ro.displayWidth = self._displayWidth 146 ro.displayHeight = self._displayHeight 147 self.UpdateBackgrounds() 148 149 return property(**locals()) 150 151 @apply 152 def left(): 153 doc = 'x-coordinate of UI element' 154 155 def fget(self): 156 return self._left 157 158 def fset(self, value): 159 adjustedValue = int(round(value)) 160 if adjustedValue != self._left: 161 self._left = adjustedValue 162 self._displayX = uicore.ScaleDpi(adjustedValue) 163 ro = self.renderObject 164 if ro: 165 ro.displayX = self._displayX 166 167 return property(**locals()) 168 169 @apply 170 def top(): 171 doc = 'y-coordinate of UI element' 172 173 def fget(self): 174 return self._top 175 176 def fset(self, value): 177 adjustedValue = int(round(value)) 178 if adjustedValue != self._top: 179 self._top = adjustedValue 180 self._displayY = uicore.ScaleDpi(adjustedValue) 181 ro = self.renderObject 182 if ro: 183 ro.displayY = self._displayY 184 185 return property(**locals()) 186 187 @apply 188 def width(): 189 doc = 'Width of UI element' 190 191 def fget(self): 192 return self._width 193 194 def fset(self, value): 195 adjustedValue = int(round(value)) 196 if adjustedValue != self._width: 197 self._width = adjustedValue 198 self._displayWidth = uicore.ScaleDpi(adjustedValue) 199 ro = self.renderObject 200 if ro: 201 ro.displayWidth = self._displayWidth 202 self.UpdateBackgrounds() 203 owner = self.owner 204 if owner is None: 205 return 206 RefreshPanelPosition(self) 207 208 return property(**locals()) 209 210 @apply 211 def height(): 212 doc = 'Height of UI element' 213 214 def fget(self): 215 return self._height 216 217 def fset(self, value): 218 adjustedValue = int(round(value)) 219 if adjustedValue != self._height: 220 self._height = adjustedValue 221 self._displayHeight = uicore.ScaleDpi(adjustedValue) 222 ro = self.renderObject 223 if ro: 224 ro.displayHeight = self._displayHeight 225 self.UpdateBackgrounds() 226 owner = self.owner 227 if owner is None: 228 return 229 RefreshPanelPosition(self) 230 231 return property(**locals()) 232 233 def GetPointerOffset(self): 234 return self.backgroundFrame.pointerOffset 235 236 def ShowPanel(self, owner): 237 blue.synchro.Yield() 238 if self.destroyed: 239 return 240 blue.synchro.SleepWallclock(2) 241 if self.destroyed: 242 return 243 uicore.animations.FadeTo(self, startVal=self.opacity, endVal=1.0, duration=0.05, curveType=uiconst.ANIM_SMOOTH) 244 245 246 class FrameWithPointer(Container): 247 pointerOffset = None 248 skinCache = {} 249 250 def ApplyAttributes(self, attributes): 251 Container.ApplyAttributes(self, attributes) 252 skinName = attributes.get('skinName', 'general') 253 actualSkinClass = SKIN_NAME_TO_CLASS.get(skinName) 254 if not FrameWithPointer.skinCache.get(skinName): 255 FrameWithPointer.skinCache[skinName] = actualSkinClass() 256 self.skin = FrameWithPointer.skinCache[skinName] 257 self._pointer = SpriteThemeColored(texturePath='res:/UI/Texture/classes/FrameWithPointer/pointer_down_02.png', parent=self, colorType=attributes.colorType or uiconst.COLORTYPE_UIHILIGHTGLOW, opacity=0.95) 258 self._background = FrameThemeColored(name='__underlay', bgParent=self, cornerSize=self.skin.backgroundCornerSize, offset=self.skin.backgroundOffset, texturePath=self.skin.backgroundTexture, colorType=attributes.colorType or uiconst.COLORTYPE_UIHILIGHTGLOW, opacity=0.95) 259 260 def SetColor(self, color): 261 self._background.color = color 262 self._pointer.color = color 263 264 def SetAlpha(self, alphaValue): 265 self._background.SetAlpha(alphaValue) 266 self._pointer.SetAlpha(alphaValue) 267 268 def UpdatePointerPosition(self, positionFlag): 269 if positionFlag == uiconst.POINTER_NONE: 270 self._pointer.display = False 271 return (0, 0) 272 SIZE = 24 273 BACKOFFSET = 8 274 x, y = positionFlag 275 self._pointer.displayX = [-SIZE + BACKOFFSET, 276 0, 277 (self.displayWidth - SIZE) / 2, 278 self.displayWidth - SIZE, 279 self.displayWidth - BACKOFFSET][x] 280 self._pointer.displayY = [-SIZE + BACKOFFSET, 281 0, 282 (self.displayHeight - SIZE) / 2, 283 self.displayHeight - SIZE, 284 self.displayHeight - BACKOFFSET][y] 285 self._pointer.displayWidth = SIZE 286 self._pointer.displayHeight = SIZE 287 if y == 0: 288 if x == 0: 289 self._pointer.displayX = -11 290 self._pointer.displayY = -11 291 resPath = self.skin.topLeftTexture 292 self.pointerOffset = [self._pointer.displayX + 5, self._pointer.displayY + 5] 293 elif x == 4: 294 self._pointer.displayX = self.displayWidth - 13 295 self._pointer.displayY = -11 296 resPath = self.skin.topRightTexture 297 self.pointerOffset = [self._pointer.displayX + 19, self._pointer.displayY + 5] 298 else: 299 resPath = self.skin.upTexture 300 self.pointerOffset = [self._pointer.displayX + SIZE / 2, self._pointer.displayY + 10] 301 elif y == 4: 302 if x == 0: 303 self._pointer.displayX = -11 304 self._pointer.displayY = self.displayHeight - 13 305 resPath = self.skin.bottomLeftTexture 306 self.pointerOffset = [self._pointer.displayX + 5, self._pointer.displayY + 19] 307 elif x == 4: 308 self._pointer.displayX = self.displayWidth - 13 309 self._pointer.displayY = self.displayHeight - 13 310 resPath = self.skin.bottomRightTexture 311 self.pointerOffset = [self._pointer.displayX + 19, self._pointer.displayY + 19] 312 else: 313 resPath = self.skin.downTexture 314 self.pointerOffset = [self._pointer.displayX + SIZE / 2, self._pointer.displayY + 14] 315 elif x == 0: 316 resPath = self.skin.leftTexture 317 self.pointerOffset = [self._pointer.displayX + 10, self._pointer.displayY + SIZE / 2] 318 elif x == 4: 319 resPath = self.skin.rightTexture 320 self.pointerOffset = [self._pointer.displayX + 14, self._pointer.displayY + SIZE / 2] 321 self._pointer.SetTexturePath(resPath) 322 self._pointer.display = True 323 return self.pointerOffset 324 325 326 def RefreshPanelPosition(pointerPanel): 327 owner = pointerPanel.owner 328 if owner is None or owner.destroyed: 329 return 330 panelPosition, isBlocked = GetPanelInterestFromObject(owner, checkIfBlockedByOther=getattr(pointerPanel, 'checkIfBlocked', True)) 331 if isBlocked: 332 pointer = pointerPanel.pointerDirection 333 else: 334 pointer = GetPanelPointerFromOwner(owner) 335 if pointer is None: 336 pointer = pointerPanel.pointerDirection 337 UpdatePanelPosition(pointerPanel, panelPosition, pointer) 338 339 340 def GetPanelInterestFromObject(uiObject, checkIfBlockedByOther = True): 341 if hasattr(uiObject, 'GetTooltipPosition'): 342 customInterestRect = uiObject.GetTooltipPosition() 343 if customInterestRect: 344 return (customInterestRect, False) 345 if uiObject is uicore.uilib.GetAuxMouseOver() and uicore.uilib.auxiliaryTooltip and uicore.uilib.auxiliaryTooltipPosition: 346 absolutePosition = uicore.uilib.auxiliaryTooltipPosition 347 else: 348 absolutePosition = uiObject.GetAbsolute() 349 retLeft, retTop, retWidth, retHeight = absolutePosition 350 retRight = retLeft + retWidth 351 retBottom = retTop + retHeight 352 obj = uiObject.parent 353 while obj: 354 l, t, w, h = obj.GetAbsolute() 355 retLeft = max(retLeft, l) 356 retTop = max(retTop, t) 357 retRight = min(retRight, l + w) 358 retBottom = min(retBottom, t + h) 359 obj = obj.parent 360 361 if checkIfBlockedByOther: 362 isBlocked = IsPartiallyBlockedByOther(uiObject, (retLeft, 363 retTop, 364 retRight, 365 retBottom)) 366 if isBlocked: 367 retLeft = max(retLeft, uicore.uilib.x - 8) 368 retTop = max(retTop, uicore.uilib.y - 8) 369 retRight = min(retRight, uicore.uilib.x + 8) 370 retBottom = min(retBottom, uicore.uilib.y + 8) 371 else: 372 isBlocked = False 373 return ((retLeft, 374 retTop, 375 retRight - retLeft, 376 retBottom - retTop), isBlocked) 377 378 379 def IsPartiallyBlockedByOther(uiObject, rect): 380 retLeft, retTop, retRight, retBottom = rect 381 hierarchyTrace = GetObjectDesktopHierarchyPosition(uiObject) 382 windows = uicore.registry.GetValidWindows() 383 for window in windows: 384 if not window.display or uiObject.IsUnder(window): 385 continue 386 windowHierarchyTrace = GetObjectDesktopHierarchyPosition(window) 387 if hierarchyTrace < windowHierarchyTrace: 388 continue 389 l2, t2, w2, h2 = window.GetAbsolute() 390 overlapx = not (retRight <= l2 or retLeft >= l2 + w2) 391 overlapy = not (retBottom <= t2 or retTop >= t2 + h2) 392 if overlapx and overlapy: 393 return True 394 395 return False 396 397 398 def SubtractBlockingUIElements(uiObject, rect): 399 retLeft, retTop, retRight, retBottom = rect 400 hierarchyTrace = GetObjectDesktopHierarchyPosition(uiObject) 401 windows = uicore.registry.GetValidWindows() 402 for window in windows: 403 if not window.display or uiObject.IsUnder(window): 404 continue 405 windowHierarchyTrace = GetObjectDesktopHierarchyPosition(window) 406 if hierarchyTrace < windowHierarchyTrace: 407 continue 408 l2, t2, w2, h2 = window.GetAbsolute() 409 overlapx = not (retRight <= l2 or retLeft >= l2 + w2) 410 overlapy = not (retBottom <= t2 or retTop >= t2 + h2) 411 if not (overlapx and overlapy): 412 continue 413 skipVertical = False 414 if retLeft < l2 < retRight: 415 retRight = l2 416 skipVertical = True 417 if retLeft < l2 + w2 < retRight: 418 retLeft = l2 + w2 419 skipVertical = True 420 if not skipVertical: 421 if retTop < t2 < retBottom: 422 retBottom = t2 423 if retTop < t2 + h2 < retBottom: 424 retTop = t2 + h2 425 426 return (retLeft, 427 retTop, 428 retRight, 429 retBottom) 430 431 432 def GetObjectDesktopHierarchyPosition(uiObject): 433 trace = [] 434 obj = uiObject 435 while obj.parent: 436 idx = obj.parent.children.index(obj) 437 trace.insert(0, idx) 438 obj = obj.parent 439 440 return trace 441 442 443 def GetPanelPointerFromOwner(uiObject): 444 if hasattr(uiObject, 'GetTooltipPointer'): 445 customPointer = uiObject.GetTooltipPointer() 446 if customPointer is not None: 447 return customPointer 448 return getattr(uiObject, 'tooltipPointer', None) 449 450 451 def _TryPanelPosition(panel, interestRect, menuPointFlag = None): 452 if menuPointFlag is None: 453 menuPointFlag = uiconst.POINT_BOTTOM_2 454 AlignPointPanelToInterest(panel, menuPointFlag, interestRect) 455 xClip, yClip = (0, 0) 456 if panel.left < 0: 457 xClip = -panel.left 458 elif panel.left + panel.width > uicore.desktop.width: 459 xClip = panel.left + panel.width - uicore.desktop.width 460 if panel.top < 0: 461 yClip = -panel.top 462 elif panel.top + panel.height > uicore.desktop.height: 463 yClip = panel.top + panel.height - uicore.desktop.height 464 return (xClip, yClip) 465 466 467 def SortTwo(a, b): 468 return (a, b if a > b else b, a) 469 470 471 def UpdatePanelPosition(panel, interestRect, menuPointFlag = None, fallbackPointFlags = None): 472 if fallbackPointFlags is None: 473 if hasattr(panel.owner, 'GetTooltipPositionFallbacks'): 474 fallbackPointFlags = panel.owner.GetTooltipPositionFallbacks() 475 else: 476 fallbackPointFlags = POINT_CORRECTION.get(menuPointFlag, [])[:] 477 fallbackPointFlags += CORNERGROUP 478 xClip, yClip = _TryPanelPosition(panel, interestRect, menuPointFlag) 479 if not (xClip or yClip): 480 return 481 cutDepth = SortTwo(xClip, yClip) 482 cutDepthToFlag = {cutDepth: menuPointFlag} 483 while cutDepth and fallbackPointFlags: 484 tryFlag = fallbackPointFlags.pop(0) 485 xClip, yClip = _TryPanelPosition(panel, interestRect, menuPointFlag=tryFlag) 486 cutDepth = SortTwo(xClip, yClip) 487 if cutDepth not in cutDepthToFlag: 488 cutDepthToFlag[cutDepth] = tryFlag 489 490 if cutDepth: 491 shallowestCut = min(cutDepthToFlag) 492 bestFlag = cutDepthToFlag[shallowestCut] 493 _TryPanelPosition(panel, interestRect, menuPointFlag=bestFlag) 494 495 496 def AlignPointPanelToInterest(panel, menuPointFlag, interestRect): 497 al, at, aw, ah = interestRect 498 if getattr(panel, 'debugShowInterest', False): 499 if getattr(panel, 'debugFrame', None): 500 panel.debugFrame.Close() 501 panel.debugFrame = Frame(parent=uicore.layer.main, pos=interestRect, align=uiconst.TOPLEFT, idx=0) 502 pointerOffset = panel.backgroundFrame.UpdatePointerPosition(menuPointFlag) 503 px, py = pointerOffset 504 px = uicore.ReverseScaleDpi(px) 505 py = uicore.ReverseScaleDpi(py) 506 fx, fy = menuPointFlag 507 panel.menuPointFlag = menuPointFlag 508 if fy == 4: 509 panel.top = at - panel.height - panel.pointerSize 510 elif fy == 0: 511 panel.top = at + ah + panel.pointerSize 512 else: 513 panel.top = at + ah / 2 - py 514 if fx == 4: 515 panel.left = al - panel.width - panel.pointerSize 516 elif fx == 0: 517 panel.left = al + aw + panel.pointerSize 518 else: 519 panel.left = al + aw / 2 - px 520 521 522 def FadeOutPanelAndClose(panel, duration = 0.2): 523 if panel.destroyed or panel.beingDestroyed: 524 return 525 panel.beingDestroyed = True 526 if getattr(panel, 'beingDestroyedCallback', None): 527 panel.beingDestroyedCallback(panel) 528 duration *= panel.opacity 529 if not duration: 530 panel.Close() 531 return 532 pointerOffset = panel.backgroundFrame.pointerOffset 533 if not pointerOffset: 534 panel.Close() 535 return 536 x, y = uicore.ReverseScaleDpi(panel.displayX + pointerOffset[0]), uicore.ReverseScaleDpi(panel.displayY + pointerOffset[1]) 537 panel.scaleTransform = Transform(parent=panel.parent, state=uiconst.UI_DISABLED, align=uiconst.TOALL, scalingCenter=(x / float(uicore.desktop.width), y / float(uicore.desktop.height))) 538 panel.parent.renderObject.children.remove(panel.renderObject) 539 panel.scaleTransform.renderObject.children.append(panel.renderObject) 540 uicore.animations.FadeTo(panel.scaleTransform, startVal=panel.opacity, endVal=0.0, duration=duration * 0.5, curveType=uiconst.ANIM_SMOOTH) 541 uicore.animations.Tr2DScaleTo(panel.scaleTransform, panel.scaleTransform.scale, (0.0, 0.0), duration=duration, callback=panel.Close)