panelOverview.py (22058B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\info\shipInfoPanels\panelOverview.py 2 import blue 3 import eveicon 4 import evetypes 5 import localization 6 import trinity 7 import uthread 8 from achievement_system.client.ui.chip.achievement_info import AchievementInfoChips 9 from carbon.client.script.environment.AudioUtil import PlaySound 10 from carbon.common.lib.const import DAY 11 from carbonui import Align, TextColor, uiconst, AxisAlignment 12 from carbonui.text.styles import TextDisplay, TextHeadline, TextHeader, TextBody, TextDetail 13 from carbonui.control.buttonIcon import ButtonIcon 14 from carbonui.control.scrollContainer import ScrollContainer 15 from carbonui.primitives.collapsibleContainer import CollapsibleContainer 16 from carbonui.primitives.container import Container 17 from carbonui.primitives.containerAutoSize import ContainerAutoSize 18 from carbonui.primitives.flowcontainer import FlowContainer 19 from carbonui.primitives.frame import Frame 20 from carbonui.primitives.line import Line 21 from carbonui.primitives.sprite import Sprite 22 from carbonui.uianimations import animations 23 from eve.client.script.ui.control.eveIcon import OwnerIcon 24 from eve.client.script.ui.shared.info.shipInfoConst import TAB_OVERVIEW, TAB_SKILLS, get_mastery_level_icon_and_message, SOUND_INSURANCE, SOUND_INSURANCE_SELECT, get_killmark_for_faction 25 from eve.client.script.ui.shared.info.shipInfoPanels.panelBase import PanelBase 26 from eve.client.script.ui.shared.stateFlag import AddAndSetFlagIcon 27 from eve.client.script.ui.shared.traits import TraitsContainer 28 from eve.client.script.ui.station.insurance.insuranceTooltip import LoadInfoInsuranceTooltip 29 from eve.client.script.ui.util.uix import GetTechLevelIconPathAndHint, GetTechLevelColor 30 from eve.common.script.util.eveFormat import FmtISK 31 from eveservices.xmppchat import GetChatService 32 from eveui import CharacterPortrait 33 from inventorycommon.typeHelpers import GetAveragePrice 34 from shipgroup import get_ship_group_name 35 36 class PanelOverview(PanelBase): 37 __notifyevents__ = ['OnShipInsured', 'OnSessionChanged', 'OnContactChange'] 38 39 def get_tagline(self): 40 quote = evetypes.GetQuoteID(self.typeID) 41 if quote: 42 return localization.GetByMessageID(quote) 43 44 def get_author(self): 45 author = evetypes.GetQuoteAuthorID(self.typeID) 46 if author: 47 return localization.GetByMessageID(author) 48 49 def ApplyAttributes(self, attributes): 50 self.insuranceParent = None 51 self._tagline = None 52 self._contact_icon = None 53 self.relationship_cont = None 54 super(PanelOverview, self).ApplyAttributes(attributes) 55 56 def _enable_minimized_view(self): 57 if self.insuranceParent: 58 self.insuranceParent.SetParent(self.basicInfoCont, idx=0) 59 self.basicInfoCont.align = Align.TOTOP 60 self.basicInfoCont.padBottom = 0 61 self.basicInfoCont.SetParent(self.content_scroll_minimized) 62 self.detailsCont.SetParent(self.content_scroll_minimized) 63 if not self.traitsContainer.destroyed: 64 self.traitsContainer.SetOrder(0) 65 if self.masteryCont: 66 self.masteryCont.height = 46 67 self.nameLabel.fontsize = TextHeadline.default_fontsize 68 if self._tagline: 69 self._tagline.fontsize = TextHeader.default_fontsize 70 if self.ownerContainer: 71 self.ownerContainer.padding = (0, 6, 0, 0) 72 73 def _enable_expanded_view(self): 74 self.basicInfoCont.align = Align.TOBOTTOM 75 self.basicInfoCont.padBottom = 0 if self.ownerContainer else 104 76 self.basicInfoCont.SetParent(self.leftCont) 77 if self.insuranceParent: 78 self.insuranceParent.SetParent(self.leftCont) 79 self.detailsCont.SetParent(self.infoScroll) 80 if not self.traitsContainer.destroyed: 81 self.traitsContainer.SetOrder(-1) 82 self.nameLabel.fontsize = TextDisplay.default_fontsize 83 if self._tagline: 84 self._tagline.fontsize = TextHeadline.default_fontsize 85 if self.masteryCont: 86 self.masteryCont.height = 64 87 if self.ownerContainer: 88 self.ownerContainer.padding = (0, 20, 0, 20) 89 90 def _construct_content(self): 91 self.infoScroll = ScrollContainer(parent=self.rightCont, align=Align.TOALL) 92 self._construct_details() 93 self._construct_basic_info() 94 self._construct_insurance_icon() 95 96 def _construct_details(self): 97 self.detailsCont = ContainerAutoSize(name='detailsCont', parent=self.infoScroll, align=Align.TOTOP) 98 tagline = self.get_tagline() 99 if tagline: 100 self._tagline = TextHeadline(parent=self.detailsCont, align=Align.TOTOP, text=tagline) 101 author = self.get_author() 102 if author: 103 TextDetail(parent=self.detailsCont, align=Align.TOTOP, text='- ' + author, color=TextColor.SECONDARY, padTop=4, padLeft=20) 104 Line(parent=self.detailsCont, align=Align.TOTOP, weight=1, padTop=10 if tagline or author else 0, padBottom=10) 105 descCont = CollapsibleContainer(parent=self.detailsCont, align=Align.TOTOP, collapsedHeight=78, padBottom=10) 106 descCont.set_text(self._controller.get_description()) 107 self.traitsContainer = TraitsContainer(parent=self.detailsCont, typeID=self.typeID, padTop=10, padBottom=20, traitAttributeIcons=True, align=Align.TOTOP) 108 109 def _construct_basic_info(self): 110 self.basicInfoCont = ContainerAutoSize(parent=self.leftCont, align=Align.TOBOTTOM, alignMode=Align.TOTOP) 111 self._construct_mastery_icon() 112 topContainer = FlowContainer(name='topContainer', parent=self.basicInfoCont, align=Align.TOTOP, contentSpacing=(8, 8), crossAxisAlignment=AxisAlignment.CENTER) 113 typeGroupContainer = ContainerAutoSize(parent=topContainer, align=Align.NOALIGN) 114 Sprite(parent=typeGroupContainer, align=Align.CENTERLEFT, width=16, height=16, texturePath=sm.GetService('bracket').GetBracketIcon(self.typeID), color=TextColor.NORMAL) 115 TextDetail(parent=typeGroupContainer, align=Align.CENTERLEFT, text=get_ship_group_name(self.typeID), padLeft=20, color=TextColor.SECONDARY) 116 iconPath, hint = GetTechLevelIconPathAndHint(typeID=self.typeID) 117 if iconPath: 118 tierContainer = ContainerAutoSize(name='tierContainer', parent=topContainer, align=Align.NOALIGN, alignMode=Align.CENTERLEFT, state=uiconst.UI_DISABLED, minHeight=16) 119 Sprite(name='techIcon', parent=tierContainer, align=Align.TOPLEFT, left=0, width=16, height=16, texturePath=iconPath, state=uiconst.UI_DISABLED) 120 Frame(bgParent=tierContainer, color=GetTechLevelColor(typeID=self.typeID), opacity=0.5, frameConst=uiconst.FRAME_FILLED_CORNER1) 121 TextDetail(parent=tierContainer, align=Align.CENTERLEFT, padLeft=16, padRight=6, text=hint, color=TextColor.NORMAL, state=uiconst.UI_DISABLED) 122 self.nameContainer = ContainerAutoSize(name='nameCont', parent=self.basicInfoCont, align=Align.TOTOP) 123 custom_name = self._controller.get_custom_name() 124 type_name = evetypes.GetName(self.typeID) 125 self.nameLabel = TextDisplay(parent=self.nameContainer, align=Align.TOTOP, text=custom_name or type_name, color=TextColor.HIGHLIGHT) 126 if custom_name: 127 TextHeader(parent=self.nameContainer, align=Align.TOTOP, text=type_name) 128 killmarkContainer = Container(name='killmarkContainer', parent=self.basicInfoCont, align=Align.TOTOP, height=24, padTop=4, display=False) 129 uthread.new(self._load_killmarks, killmarkContainer) 130 self.construct_est_price() 131 self._construct_pilot() 132 133 def _construct_pilot(self): 134 ownerID = self.ownerID 135 self.ownerContainer = None 136 if not ownerID: 137 return 138 if ownerID == session.charid and self.itemID != session.shipid: 139 return 140 self.ownerContainer = Container(name='ownerCont', parent=self.basicInfoCont, align=Align.TOTOP, height=72, padTop=20, padBottom=20) 141 portraitContainer = Container(name='portraitCont', parent=self.ownerContainer, align=Align.TOLEFT, width=64, height=72) 142 CharacterPortrait(parent=portraitContainer, align=Align.CENTER, size=64, character_id=ownerID, textureSecondaryPath='res:/UI/Texture/circle_full.png', spriteEffect=trinity.TR2_SFX_MODULATE) 143 infoCont = Container(name='infoCont', parent=self.ownerContainer, align=Align.TOALL, padLeft=8) 144 self.allianceCont = Container(name='allianceCont', parent=infoCont, align=Align.TOBOTTOM, height=24, itemSpacing=(10, 0), state=uiconst.UI_PICKCHILDREN, padBottom=4) 145 AchievementInfoChips(name='achievement_info_chips', parent=infoCont, align=Align.TOBOTTOM, character_id=ownerID, display_score=False, padding=(0, 4, 0, 6)) 146 nameCont = Container(name='nameCont', parent=infoCont, align=Align.TOALL) 147 TextHeader(parent=ContainerAutoSize(parent=nameCont, align=Align.TOLEFT), align=Align.CENTERLEFT, text=cfg.eveowners.Get(ownerID).name) 148 self.relationship_cont = Container(parent=ContainerAutoSize(parent=nameCont, align=Align.TOLEFT, padLeft=10), align=Align.CENTERLEFT, padTop=1, width=16, height=16) 149 uthread.new(self.load_character_info) 150 if ownerID != session.charid: 151 isContact = sm.GetService('addressbook').IsInAddressBook(ownerID, 'contact') 152 if isContact: 153 hint = localization.GetByLabel('UI/PeopleAndPlaces/EditContact') 154 else: 155 hint = localization.GetByLabel('UI/PeopleAndPlaces/AddContact') 156 self._contact_icon = ButtonIcon(name='addContactIcon', parent=ContainerAutoSize(parent=self.allianceCont, align=Align.TORIGHT, padRight=4), align=Align.CENTER, texturePath=eveicon.user_plus, pos=(0, 0, 16, 16), iconSize=16, func=lambda : sm.GetService('addressbook').AddToPersonalMulti([ownerID], 'contact', True), hint=hint) 157 ButtonIcon(name='chatIcon', parent=ContainerAutoSize(parent=self.allianceCont, align=Align.TORIGHT, padRight=4), align=Align.CENTER, texturePath=eveicon.chat_bubble, pos=(0, 0, 16, 16), iconSize=16, func=lambda *args: self._on_chat_button(ownerID), hint=localization.GetByLabel('UI/Chat/StartConversation')) 158 159 def _construct_insurance_icon(self): 160 if self.insuranceParent: 161 self.insuranceParent.Flush() 162 self.insuranceParent.display = False 163 else: 164 self.insuranceParent = Container(name='insuranceParent', parent=self.leftCont, align=uiconst.TOTOP_NOPUSH, height=36, display=False) 165 if not self._should_show_insurance(): 166 return 167 contract = sm.GetService('insurance').GetContractForShip(self.itemID) 168 price = sm.GetService('insurance').GetInsurancePrice(self.typeID) 169 if price <= 0: 170 self.insuranceParent.display = True 171 return 172 status = InsuranceIcon.STATUS_OK 173 if contract and contract.ownerID in (session.corpid, session.charid): 174 timeDiff = contract.endDate - blue.os.GetWallclockTime() 175 days = timeDiff / DAY 176 if days < 5: 177 status = InsuranceIcon.STATUS_CAUTION 178 else: 179 status = InsuranceIcon.STATUS_WARNING 180 insuranceIcon = InsuranceIcon(parent=self.insuranceParent, state=uiconst.UI_NORMAL, itemID=self.itemID, status=status) 181 insuranceIcon.LoadTooltipPanel = lambda tooltipPanel, *args: LoadInfoInsuranceTooltip(tooltipPanel, self.typeID, contract) 182 self.insuranceParent.display = True 183 184 def _should_show_insurance(self): 185 return self._controller.is_assembled and self._controller.is_owned_by_me 186 187 def _construct_mastery_icon(self): 188 self.masteryCont = None 189 ownerID = self.ownerID 190 if ownerID and ownerID != session.charid: 191 return 192 self.masteryCont = Container(name='masteryCont', parent=self.basicInfoCont, align=Align.TOTOP, height=64) 193 texturePath, message, onClick = get_mastery_level_icon_and_message(self.typeID) 194 masterySprite = Sprite(name='masterySprite', parent=self.masteryCont, align=Align.CENTERLEFT, height=64, width=64, texturePath=texturePath, hint=message, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.1) 195 if not onClick: 196 onClick = lambda *args, **kwargs: self._controller.select_tab(TAB_SKILLS) 197 masterySprite.OnClick = onClick 198 199 def _on_chat_button(self, characterID): 200 GetChatService().Invite(characterID) 201 202 def load_character_info(self): 203 if self.ownerID is None: 204 return 205 if self.ownerID == session.charid and self.allianceCont: 206 self._load_organization_logos(self.allianceCont, session.corpid, session.allianceid, session.warfactionid) 207 else: 208 owner_data = self._controller.get_owner_data() 209 if not owner_data: 210 return 211 self._load_relationship() 212 self._load_organization_logos(self.allianceCont, owner_data.corpID, owner_data.allianceID, owner_data.warFactionID) 213 214 def _reload_character_info(self): 215 self.allianceCont.Flush() 216 self.relationship_cont.Flush() 217 self.load_character_info() 218 219 def _load_relationship(self): 220 if not self.relationship_cont: 221 return 222 owner_data = self._controller.get_owner_data() 223 if not owner_data: 224 return 225 flag = sm.GetService('stateSvc').CheckStates(owner_data, 'flag') 226 AddAndSetFlagIcon(self.relationship_cont, flag=flag, align=Align.CENTER, width=16, height=16) 227 228 def _load_organization_logos(self, container, corpID, allianceID, warFactionID): 229 if corpID: 230 corpName = cfg.eveowners.Get(corpID).name 231 OwnerIcon(parent=Container(parent=container, align=Align.TOLEFT, width=24, padRight=8), align=Align.CENTER, ownerID=corpID, isSmall=True, width=24, height=24, hint=localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Corporation', corpName=corpName)) 232 if allianceID: 233 allianceName = cfg.eveowners.Get(allianceID).name 234 OwnerIcon(parent=Container(parent=container, align=Align.TOLEFT, width=24, padRight=8), align=Align.CENTER, ownerID=allianceID, isSmall=True, width=24, height=24, hint=localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/Alliance', allianceName=allianceName)) 235 if warFactionID: 236 factionName = cfg.eveowners.Get(warFactionID).name 237 OwnerIcon(parent=Container(parent=container, align=Align.TOLEFT, width=24, padRight=8), align=Align.CENTER, ownerID=warFactionID, isSmall=True, width=24, height=24, hint=localization.GetByLabel('UI/CharacterSheet/CharacterSheetWindow/FactionalWarfare', factionName=factionName)) 238 239 def _load_killmarks(self, killmarkContainer): 240 if self.itemID is None or self.ownerID is None or self._controller.is_simulated: 241 return 242 killmarksCount = sm.RemoteSvc('shipKillCounter').GetItemKillCountPlayer(self.itemID) 243 if not killmarksCount: 244 return 245 factionID = evetypes.GetFactionID(self.typeID) 246 killmarkIcon = get_killmark_for_faction(factionID) 247 Sprite(parent=killmarkContainer, state=uiconst.UI_DISABLED, align=Align.CENTERLEFT, texturePath=killmarkIcon, color=TextColor.NORMAL, width=16, height=16) 248 TextBody(parent=killmarkContainer, align=Align.CENTERLEFT, padLeft=20, text=localization.GetByLabel('UI/InfoWindow/KillCount', count=killmarksCount, countColor=TextColor.NORMAL), color=TextColor.SECONDARY) 249 killmarkContainer.display = True 250 251 def construct_est_price(self): 252 priceCont = Container(name='priceCont', parent=self.basicInfoCont, align=Align.TOTOP, height=24) 253 labelCont = ContainerAutoSize(name='labelCont', parent=priceCont, align=Align.TOLEFT) 254 TextDetail(parent=labelCont, align=Align.BOTTOMLEFT, color=TextColor.SECONDARY, text=localization.GetByLabel('UI/Inventory/EstPrice').lower()) 255 valueCont = Container(name='valueCont', parent=priceCont, align=Align.TOALL, padLeft=10) 256 price = GetAveragePrice(self.typeID) or 0 257 TextDetail(parent=valueCont, align=Align.BOTTOMLEFT, text=FmtISK(price), color=TextColor.NORMAL, padBottom=1) 258 if not price: 259 priceCont.Hide() 260 261 @classmethod 262 def get_name(cls): 263 return localization.GetByLabel('UI/InfoWindow/TabNames/Overview') 264 265 @classmethod 266 def get_icon(cls): 267 return eveicon.info 268 269 @classmethod 270 def is_visible(cls, typeID, itemID = None, rec = None): 271 return True 272 273 def get_zoom(self): 274 return 0.3 275 276 def get_tab_type(self): 277 return TAB_OVERVIEW 278 279 def OnShipInsured(self): 280 self._construct_insurance_icon() 281 282 def OnSessionChanged(self, _isRemote, _session, change): 283 if not self.ownerID or self.ownerID != session.charid: 284 return 285 if 'corpid' in change or 'allianceid' in change or 'warfactionid' in change: 286 self._reload_character_info() 287 288 def OnContactChange(self, _contactIDs, _contactType = None): 289 if not self.relationship_cont: 290 return 291 self.relationship_cont.Flush() 292 self._load_relationship() 293 isContact = sm.GetService('addressbook').IsInAddressBook(self.ownerID, 'contact') 294 self._contact_icon.hint = localization.GetByLabel('UI/PeopleAndPlaces/EditContact') if isContact else localization.GetByLabel('UI/PeopleAndPlaces/AddContact') 295 296 297 class InsuranceIcon(Container): 298 EXPANDED_WIDTH = 114 299 MINIMIZED_WIDTH = 43 300 COLOR_INSURED_LIGHT = (0.35, 0.65, 0.75, 0.4) 301 COLOR_INSURED_DARK = (0.19, 0.34, 0.4, 0.4) 302 COLOR_INSURED_TEXT = (0.35, 0.65, 0.75, 1) 303 COLOR_CAUTION_LIGHT = (0.95, 0.56, 0.345, 0.8) 304 COLOR_CAUTION_DARK = (0.953, 0.565, 0.345, 0.4) 305 COLOR_CAUTION_TEXT = TextColor.WARNING 306 COLOR_UNINSURED_LIGHT = (1, 0.27, 0.29, 0.4) 307 COLOR_UNINSURED_DARK = (0.6, 0.12, 0.14, 0.4) 308 COLOR_UNINSURED_TEXT = (1, 0.27, 0.29, 1) 309 STATUS_OK = 1 310 STATUS_CAUTION = 2 311 STATUS_WARNING = 3 312 COLOR_LIGHT = {STATUS_OK: COLOR_INSURED_LIGHT, 313 STATUS_CAUTION: COLOR_CAUTION_LIGHT, 314 STATUS_WARNING: COLOR_UNINSURED_LIGHT} 315 COLOR_DARK = {STATUS_OK: COLOR_INSURED_DARK, 316 STATUS_CAUTION: COLOR_CAUTION_DARK, 317 STATUS_WARNING: COLOR_UNINSURED_DARK} 318 COLOR_TEXT = {STATUS_OK: COLOR_INSURED_TEXT, 319 STATUS_CAUTION: COLOR_CAUTION_TEXT, 320 STATUS_WARNING: COLOR_UNINSURED_TEXT} 321 default_align = Align.TOPRIGHT 322 default_height = 36 323 default_width = MINIMIZED_WIDTH 324 325 def ApplyAttributes(self, attributes): 326 self.EXPANDED_WIDTH = 114 327 self.MINIMIZED_WIDTH = 43 328 super(InsuranceIcon, self).ApplyAttributes(attributes) 329 self.itemID = attributes.itemID 330 self.status = attributes.status 331 self.construct_layout() 332 self.calculate_expanded_width() 333 334 def construct_layout(self): 335 self.content = Container(parent=self, align=Align.TOALL) 336 self.bg_frame = Frame(parent=self, align=uiconst.TOALL, texturePath='res:/UI/Texture/classes/Button/background_cut_bottom_left.png', cornerSize=9, color=(0, 0, 0, 0.4)) 337 self.right_line_light = Container(parent=self.content, align=Align.TORIGHT, width=1, bgColor=self.get_color_light()) 338 self.right_line_dark = Container(parent=self.content, align=Align.TORIGHT, width=6, bgColor=self.get_color_dark()) 339 self.icon_cont = Container(parent=self.content, align=Align.TOLEFT, width=32, padLeft=2) 340 Sprite(name='ship', parent=self.icon_cont, align=Align.CENTER, width=16, height=16, texturePath=eveicon.spaceship_command) 341 self.shield_left = Sprite(name='shieldLeft', parent=self.icon_cont, align=Align.CENTER, width=32, height=32, texturePath='res:/UI/Texture/classes/ShipInfo/shield_left.png', color=self.get_color_light()) 342 self.shield_right = Sprite(name='shieldRight', parent=self.icon_cont, align=Align.CENTER, width=32, height=32, texturePath='res:/UI/Texture/classes/ShipInfo/shield_right.png', color=self.get_color_dark()) 343 self.text_cont = Container(parent=self.content, align=Align.TOLEFT, width=73, padLeft=2, opacity=0) 344 label_cont = Container(parent=self.text_cont, align=Align.TOTOP_PROP, height=0.5, padTop=2) 345 status_cont = Container(parent=self.text_cont, align=Align.TOTOP_PROP, height=0.5, padBottom=2) 346 self.label = TextDetail(parent=label_cont, align=Align.CENTERLEFT, text=localization.GetByLabel('UI/Insurance/InsuranceStatusLabel')) 347 self.status_label = TextDetail(parent=status_cont, align=Align.CENTERLEFT, text=self.get_status_text(), color=self.get_color_text()) 348 349 def OnMouseEnter(self, *args): 350 animations.MorphScalar(self, 'width', startVal=self.ReverseScaleDpi(self.displayWidth), endVal=self.EXPANDED_WIDTH, duration=0.2) 351 animations.FadeIn(self.text_cont, duration=0.2, timeOffset=0.1) 352 PlaySound(SOUND_INSURANCE) 353 354 def OnMouseExit(self, *args): 355 animations.MorphScalar(self, 'width', startVal=self.ReverseScaleDpi(self.displayWidth), endVal=self.MINIMIZED_WIDTH, duration=0.2) 356 animations.FadeOut(self.text_cont, duration=0.1) 357 358 def OnClick(self, *args): 359 sm.GetService('cmd').OpenInsurance() 360 PlaySound(SOUND_INSURANCE_SELECT) 361 362 def get_color_light(self): 363 return self.COLOR_LIGHT[self.status] 364 365 def get_color_dark(self): 366 return self.COLOR_DARK[self.status] 367 368 def get_color_text(self): 369 return self.COLOR_TEXT[self.status] 370 371 def get_status_text(self): 372 if self.status == self.STATUS_WARNING: 373 return localization.GetByLabel('UI/Insurance/InsuranceStatusUninsured') 374 else: 375 return localization.GetByLabel('UI/Insurance/InsuranceStatusInsured') 376 377 def calculate_expanded_width(self): 378 label_width, _ = TextBody.MeasureTextSize(self.label.text) 379 status_width, _ = TextBody.MeasureTextSize(self.status_label.text) 380 text_width = max(label_width, status_width) + 12 381 icon_width = 32 382 self.EXPANDED_WIDTH = max(self.EXPANDED_WIDTH, text_width + icon_width)