infoPanelContainer.py (14902B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\infoPanels\infoPanelContainer.py 2 import logging 3 import blue 4 import telemetry 5 import carbonui.const as uiconst 6 import eve.client.script.ui.shared.pointerTool.pointerToolConst as pConst 7 import uthread 8 import uthread2 9 from carbonui.primitives.container import Container 10 from carbonui.primitives.containerAutoSize import ContainerAutoSize 11 from carbonui.primitives.line import Line 12 from carbonui.uicore import uicore 13 from carbonui.util.color import Color 14 from carbonui.control.buttonIcon import ButtonIcon 15 from eve.client.script.ui.shared.infoPanels import infoPanelSettingsController 16 from eve.client.script.ui.shared.infoPanels.const import infoPanelConst, infoPanelClassConst, infoPanelUIConst 17 from eve.client.script.ui.shared.infoPanels.infoPanelDragData import InfoPanelDragData 18 from operations.common.util import get_tutorial_category_id 19 from uihider import get_ui_hider, UiHiderMixin 20 logger = logging.getLogger(__name__) 21 22 class InfoPanelContainer(UiHiderMixin, ContainerAutoSize): 23 default_name = 'InfoPanelContainer' 24 uniqueUiName = pConst.UNIQUE_NAME_INFO_PANEL 25 ICONSIZE = 16 26 __notifyevents__ = ['OnOperationsInitialized', 'OnUIScalingChange'] 27 default_performFitsCheck = True 28 29 def ApplyAttributes(self, attributes): 30 ContainerAutoSize.ApplyAttributes(self, attributes) 31 self.panelClasses = [] 32 settingsID = attributes.get('settingsID', None) 33 self.performFitsCheck = attributes.get('performFitsCheck', self.default_performFitsCheck) 34 self.infoPanelsByTypeID = {} 35 self.infoPanelButtonsByTypeID = {} 36 self.isDraggingButton = False 37 self.ConstructLayout() 38 self.ReconstructPanels(settingsID, False) 39 sm.RegisterNotify(self) 40 41 def ConstructLayout(self): 42 self.topCont = Container(name='topCont', parent=self, align=uiconst.TOTOP, height=self.ICONSIZE, padding=(infoPanelUIConst.LEFTPAD, 43 0, 44 0, 45 5)) 46 self.dropIndicatorLine = Line(name='dropIndicatorLine', parent=self.topCont, align=uiconst.TOLEFT_NOPUSH, state=uiconst.UI_HIDDEN, color=Color.GetGrayRGBA(1.0, 0.6), padding=(0, 2, 0, 2)) 47 self.iconCont = ContainerAutoSize(name='iconCont', parent=self.topCont, state=uiconst.UI_NORMAL, align=uiconst.TOLEFT, uniqueUiName=pConst.UNIQUE_NAME_INFO_PANEL_BTNS) 48 self.mainCont = ContainerAutoSize(name='mainCont', parent=self, align=uiconst.TOTOP) 49 50 def ReconstructPanels(self, settingsID, animate = True): 51 self.settingsID = settingsID 52 if settingsID: 53 self._ReconstructPanels(animate) 54 55 def _GetCurrentlyAvailableInfoPanelClasses(self): 56 panelIDs = infoPanelConst.PANELS_BY_SETTINGSID.get(self.settingsID, []) 57 infoPanelClasses = [ infoPanelClassConst.PANEL_TYPE_ID_TO_CLASS[panelID] for panelID in panelIDs ] 58 availableInfoPanelClasses = [] 59 for infoPanelClass in infoPanelClasses: 60 try: 61 if infoPanelClass.IsAvailable(): 62 availableInfoPanelClasses.append(infoPanelClass) 63 except Exception as exc: 64 logger.exception(exc) 65 continue 66 67 return availableInfoPanelClasses 68 69 def IsPanelTypeAllowedInView(self, panelTypeID): 70 return panelTypeID in infoPanelConst.PANELS_BY_SETTINGSID.get(self.settingsID, []) 71 72 def SortPanelClasses(self): 73 if infoPanelSettingsController.get_all_info_panel_settings(self.settingsID): 74 self.panelClasses = sorted(self.panelClasses, key=self.GetInfoPanelClassKey) 75 76 def GetInfoPanelClassKey(self, infoPanelClass): 77 panelSettings = infoPanelSettingsController.get_all_info_panel_settings(self.settingsID) 78 for index, entry in enumerate(panelSettings): 79 if entry[0] == infoPanelClass.panelTypeID: 80 return index 81 82 if infoPanelClass in self.panelClasses: 83 return self.panelClasses.index(infoPanelClass) 84 85 def OnOperationsInitialized(self, categoryID, operationID): 86 if categoryID == get_tutorial_category_id(): 87 self.ConstructTopIcons() 88 89 def OnUIScalingChange(self, *args): 90 self._ReconstructPanels() 91 92 @telemetry.ZONE_FUNCTION 93 def ConstructTopIcons(self): 94 self.iconCont.Flush() 95 self.infoPanelButtonsByTypeID.clear() 96 for infoPanelCls in self.panelClasses: 97 self.ConstructTopIcon(infoPanelCls) 98 99 def ConstructTopIcon(self, infoPanelCls): 100 if infoPanelCls.panelTypeID in self.infoPanelButtonsByTypeID: 101 return 102 if not infoPanelCls.IsAvailable() or get_ui_hider().is_ui_element_hidden(infoPanelCls.uniqueUiName): 103 return 104 try: 105 button = ButtonIconInfoPanel(infoPanelCls=infoPanelCls, parent=self.iconCont, controller=self, texturePath=infoPanelCls.GetTexturePath(), func=self.OnPanelContainerIconPressed, align=uiconst.TOLEFT, width=self.ICONSIZE) 106 except: 107 logger.exception('InfoPanelContainer::ConstructTopIcons - Unable to construct top icon for info panel class: %s' % infoPanelCls) 108 else: 109 self.infoPanelButtonsByTypeID[infoPanelCls.panelTypeID] = button 110 111 def OnPanelContainerIconPressed(self, panelTypeID): 112 panel = self.GetPanelByTypeID(panelTypeID) 113 if panel: 114 if panel.isInModeTransition: 115 return 116 if panel.mode == infoPanelConst.MODE_HIDDEN: 117 panel.mode = infoPanelConst.MODE_NORMAL 118 elif panel.isCollapsable: 119 panel.mode = infoPanelConst.MODE_HIDDEN 120 elif panel.mode == infoPanelConst.MODE_NORMAL: 121 panel.mode = infoPanelConst.MODE_COMPACT 122 else: 123 panel.mode = infoPanelConst.MODE_NORMAL 124 125 @telemetry.ZONE_FUNCTION 126 def _ConstructInfoPanels(self): 127 for infoPanelCls in self.panelClasses: 128 if not session.charid: 129 return 130 self.ConstructInfoPanel(infoPanelCls) 131 132 if self.performFitsCheck: 133 self.CheckAllPanelsFit() 134 135 def CheckAllPanelsFit(self, triggeredByPanel = None): 136 uthread2.StartTasklet(self._CheckAllPanelsFit, triggeredByPanel) 137 138 @telemetry.ZONE_FUNCTION 139 def _CheckAllPanelsFit(self, triggeredByPanel = None): 140 for mode in (infoPanelConst.MODE_COMPACT, infoPanelConst.MODE_HIDDEN): 141 for panelTypeID, panel in self.infoPanelsByTypeID.iteritems(): 142 if panel.mode == infoPanelConst.MODE_HIDDEN: 143 continue 144 if not self.IsLastPanelClipped(): 145 return 146 if panelTypeID == triggeredByPanel: 147 continue 148 panel.mode = mode 149 logger.info("InfoPanelContainer::_CheckAllPanelsFit - Panels don't fit, setting panelTypeID %s mode to %s", panelTypeID, mode) 150 151 def ConstructInfoPanel(self, infoPanelClass): 152 if infoPanelClass not in self.panelClasses: 153 self.panelClasses.append(infoPanelClass) 154 mode = infoPanelSettingsController.get_panel_mode(self.settingsID, infoPanelClass.panelTypeID) 155 try: 156 panel = infoPanelClass(parent=self.mainCont, mode=mode, settingsID=self.settingsID) 157 except: 158 logger.exception('InfoPanelContainer::ConstructInfoPanels - Unable to construct info panel: %s' % infoPanelClass) 159 else: 160 panel.onModeTransitionToNormalSignal.connect(self.CheckAllPanelsFit) 161 self.infoPanelsByTypeID[infoPanelClass.panelTypeID] = panel 162 if not infoPanelClass.IsAvailable(): 163 panel.Hide() 164 self.ConstructTopIcon(infoPanelClass) 165 finally: 166 blue.synchro.Yield() 167 168 def UpdatePanel(self, panelTypeID, ignoreViewSettings = True): 169 panel = self.GetPanelByTypeID(panelTypeID) 170 if not panel or panel.destroyed: 171 panelCls = infoPanelClassConst.PANEL_TYPE_ID_TO_CLASS.get(panelTypeID, None) 172 if panelCls.IsAvailable(): 173 if ignoreViewSettings or self.IsPanelTypeAllowedInView(panelTypeID): 174 self.ConstructInfoPanel(panelCls) 175 elif panel and not panel.IsAvailable(): 176 self.ClosePanel(panelTypeID) 177 178 @telemetry.ZONE_FUNCTION 179 def _ReconstructPanels(self, animate = False): 180 uthread.Lock(self) 181 self.panelClasses = self._GetCurrentlyAvailableInfoPanelClasses() 182 try: 183 self.SortPanelClasses() 184 if animate: 185 self.mainCont.opacity = 0.0 186 if not session.charid: 187 return 188 self.mainCont.Flush() 189 self._ConstructInfoPanels() 190 if not session.charid: 191 return 192 self.ConstructTopIcons() 193 uicore.animations.FadeIn(self.mainCont, duration=0.6) 194 finally: 195 uthread.UnLock(self) 196 197 @telemetry.ZONE_FUNCTION 198 def ClosePanel(self, panelTypeID): 199 panel = self.GetPanelByTypeID(panelTypeID) 200 button = self.GetPanelButtonByTypeID(panelTypeID) 201 if panel: 202 self.infoPanelsByTypeID.pop(panelTypeID) 203 panel.Close() 204 if button: 205 self.infoPanelButtonsByTypeID.pop(panelTypeID) 206 button.Close() 207 208 def GetPanelByTypeID(self, panelTypeID): 209 return self.infoPanelsByTypeID.get(panelTypeID, None) 210 211 def GetPanelButtonByTypeID(self, panelTypeID): 212 return self.infoPanelButtonsByTypeID.get(panelTypeID, None) 213 214 def OnButtonDragStart(self, infoPanelCls): 215 self.isDraggingButton = infoPanelCls 216 uicore.uilib.RegisterForTriuiEvents(uiconst.UI_MOUSEMOVE, self.OnButtonDragMove) 217 218 def OnButtonDragEnd(self, infoPanelCls): 219 self.dropIndicatorLine.Hide() 220 self.isDraggingButton = False 221 if self.IsDraggingOverIcons(): 222 idx = self.GetDropIconIdx() 223 dragId = self.GetCurrentPanelTypes().index(infoPanelCls.panelTypeID) 224 if dragId < idx: 225 idx -= 1 226 if idx < len(self.mainCont.children): 227 currAtIdx = self.mainCont.children[idx] 228 oldTypeID = currAtIdx.panelTypeID 229 else: 230 oldTypeID = None 231 self.MovePanelInFrontOf(infoPanelCls, oldTypeID) 232 233 @telemetry.ZONE_FUNCTION 234 def MovePanelInFrontOf(self, infoPanelCls, oldTypeID = None): 235 panelSettings = infoPanelSettingsController.get_all_info_panel_settings(self.settingsID) 236 entry = infoPanelSettingsController.get_panel_settings_entry_by_type(panelSettings, infoPanelCls.panelTypeID) 237 if oldTypeID: 238 idx = panelSettings.index(infoPanelSettingsController.get_panel_settings_entry_by_type(panelSettings, oldTypeID)) 239 else: 240 idx = len(self.mainCont.children) 241 oldIdx = panelSettings.index(infoPanelSettingsController.get_panel_settings_entry_by_type(panelSettings, infoPanelCls.panelTypeID)) 242 if oldIdx == idx: 243 return 244 panelSettings.pop(oldIdx) 245 panelSettings.insert(idx, entry) 246 infoPanelSettingsController.save_info_panel_setting(self.settingsID, panelSettings) 247 self._ReconstructPanels(animate=True) 248 249 def OnButtonDragMove(self, *args): 250 if not self.isDraggingButton: 251 return False 252 if self.IsDraggingOverIcons(): 253 self.dropIndicatorLine.state = uiconst.UI_DISABLED 254 self.SetDropIndicatorLinePosition() 255 else: 256 self.dropIndicatorLine.Hide() 257 return True 258 259 def IsDraggingOverIcons(self): 260 return uicore.uilib.mouseOver.IsUnder(self.iconCont) or uicore.uilib.mouseOver == self.iconCont 261 262 def GetCurrentPanelTypes(self): 263 return [ panel.panelTypeID for panel in self.panelClasses ] 264 265 def SetDropIndicatorLinePosition(self): 266 idx = self.GetDropIconIdx() 267 dragIdx = self.GetCurrentPanelTypes().index(self.isDraggingButton.panelTypeID) 268 if idx in (dragIdx, dragIdx + 1): 269 self.dropIndicatorLine.Hide() 270 else: 271 self.dropIndicatorLine.Show() 272 numIcons = self.GetNumVisible() 273 left, _, width, _ = self.iconCont.GetAbsolute() 274 self.dropIndicatorLine.left = idx * (width / numIcons) - 2 275 276 def GetDropIconIdx(self): 277 numIcons = self.GetNumVisible() 278 left, _, width, _ = self.iconCont.GetAbsolute() 279 x = uicore.uilib.x - left 280 numSlots = numIcons * 2 281 pos = max(0, min(x / float(width), 1.0)) 282 idx = numSlots * pos 283 idx = int(float(idx) / 2 + 0.5) 284 return idx 285 286 def GetNumVisible(self): 287 return len([ child for child in self.iconCont.children if child.display ]) 288 289 def IsLastPanelClipped(self): 290 if self.destroyed: 291 return False 292 if not self.mainCont.children: 293 return False 294 lastChild = self.GetLastVisiblePanel() 295 if not lastChild: 296 return False 297 _, pt, _, ph = self.parent.GetAbsolute() 298 if ph <= 0: 299 logger.warn("InfoPanelContainer::IsLastPanelClipped: Container parent height is %s (zero or less) so don't collapse any panels", ph) 300 return False 301 _, t, _, h = lastChild.GetAbsolute() 302 return t - pt + h > ph 303 304 def GetLastVisiblePanel(self): 305 children = self.mainCont.children[:] 306 children.reverse() 307 for child in children: 308 if child.mode != infoPanelConst.MODE_HIDDEN: 309 return child 310 311 312 class ButtonIconInfoPanel(ButtonIcon): 313 __notifyevents__ = ['OnInfoPanelSettingChanged'] 314 default_padRight = 4 315 default_iconSize = 18 316 isDragObject = True 317 318 def ApplyAttributes(self, attributes): 319 ButtonIcon.ApplyAttributes(self, attributes) 320 sm.RegisterNotify(self) 321 self.controller = attributes.controller 322 self.infoPanelCls = attributes.infoPanelCls 323 self.UpdateMode(self.GetInfoPanelMode()) 324 325 def OnClick(self, *args): 326 self.func(self.infoPanelCls.panelTypeID) 327 328 def GetHint(self): 329 return self.infoPanelCls.GetClassHint() 330 331 def OnInfoPanelSettingChanged(self, panelTypeID, mode): 332 if panelTypeID == self.infoPanelCls.panelTypeID: 333 self.UpdateMode(mode) 334 335 def UpdateMode(self, mode): 336 if mode == infoPanelConst.MODE_HIDDEN: 337 self.SetSelected() 338 else: 339 self.SetDeselected() 340 341 def GetInfoPanelMode(self): 342 return infoPanelSettingsController.get_panel_mode(self.controller.settingsID, self.infoPanelCls.panelTypeID) 343 344 def GetDragData(self): 345 self.controller.OnButtonDragStart(self.infoPanelCls) 346 return (InfoPanelDragData(self.infoPanelCls),) 347 348 def OnEndDrag(self, *args): 349 ButtonIcon.OnEndDrag(self, *args) 350 self.controller.OnButtonDragEnd(self.infoPanelCls) 351 352 def OnDblClick(self, *args): 353 pass