recommendationCont.py (20272B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\recommendation\recommendationCont.py 2 import mathext 3 import uthread2 4 from carbon.client.script.environment.AudioUtil import PlaySound 5 from carbonui import ButtonStyle, fontconst, uiconst 6 from carbonui.control.button import Button 7 from carbonui.primitives.container import Container 8 from carbonui.primitives.containerAutoSize import ContainerAutoSize 9 from carbonui.primitives.fill import Fill 10 from carbonui.primitives.frame import Frame 11 from carbonui.primitives.layoutGrid import LayoutGrid 12 from carbonui.primitives.sprite import Sprite 13 from carbonui.primitives.stretchspritehorizontal import StretchSpriteHorizontal 14 from carbonui.uianimations import animations 15 from carbonui.util.color import Color 16 from eve.client.script.ui.control.moreIcon import DescriptionIcon 17 from eve.client.script.ui.control.eveLabel import Label, EveLabelMedium 18 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel 19 from eve.client.script.ui.shared.agencyNew.ui.agencyWnd import AgencyWndNew 20 from eve.client.script.ui.shared.recommendation.const import ANOTHER_ACTIVE, COMPLETED, CURRENT_ACTIVE, NO_ACTIVE, NUM_SLOTS, REJECTED, Sounds, UPDATE_TIMEOUT_SECONDS 21 from eve.client.script.ui.shared.recommendation.uiConst import LIGHT_GREEN, LIGHT_YELLOW, MAIN_GREEN, MAIN_YELLOW 22 from localization import GetByLabel 23 from player_messaging.client.ui_message import prompt_player_fsd_dialog 24 ANIM_DURATION = 0.225 25 DECO_OPACITY = 0.5 26 ICON_FILL_OPACITY = 0.75 27 28 class RecommendationsCont(Container): 29 default_align = uiconst.CENTERTOP 30 default_width = 935 31 default_height = 415 32 33 def __init__(self, **kwargs): 34 super(RecommendationsCont, self).__init__(**kwargs) 35 self.allCards = [] 36 self.recommendationSvc = sm.GetService('recommendationSvc') 37 uthread2.StartTasklet(self.ConstructLayout) 38 39 def ConstructLayout(self): 40 labelCont = ContainerAutoSize(parent=self, minHeight=65, align=uiconst.TOTOP) 41 Label(name='header', parent=labelCont, align=uiconst.TOTOP, text='<center>%s</center>' % GetByLabel('UI/recommendations/OpportunitiesHeader'), fontsize=18, top=12) 42 Label(name='text', parent=labelCont, align=uiconst.TOTOP, text='<center>%s</center>' % GetByLabel('UI/recommendations/OpportunitiesDescription'), fontsize=13) 43 treatmentsCont = Container(parent=self) 44 mainGrid = LayoutGrid(columns=3, parent=treatmentsCont, align=uiconst.CENTERTOP, cellSpacing=20) 45 shouldBlinkBtn = self.recommendationSvc.ShouldBtnsBlink() 46 self.treatmentCard1 = RecommendationCard(parent=mainGrid, slotIdx=0, shouldBlinkBtn=shouldBlinkBtn) 47 self.treatmentCard2 = RecommendationCard(parent=mainGrid, slotIdx=1, shouldBlinkBtn=shouldBlinkBtn) 48 self.treatmentCard3 = RecommendationCard(parent=mainGrid, slotIdx=2, shouldBlinkBtn=shouldBlinkBtn) 49 self.allCards = [self.treatmentCard1, self.treatmentCard2, self.treatmentCard3] 50 self.LoadCards() 51 52 def LoadCards(self): 53 if not self.recommendationSvc.HasRecommendations(): 54 self.recommendationSvc.RequestRecommendations(NUM_SLOTS, allowOld=True) 55 for slotIdx, card in enumerate(self.allCards): 56 self.LoadCard(card, slotIdx) 57 58 def LoadCard(self, card, slotIdx): 59 recommendationController = self.recommendationSvc.GetRecommendationControllerForSlot(slotIdx) 60 if not recommendationController: 61 return 62 card.recommendationController = recommendationController 63 card.LoadCard() 64 65 66 class RecommendationCard(Container): 67 default_align = uiconst.CENTERLEFT 68 default_height = 326 69 default_width = 265 70 __notifyevents__ = ['OnOperationDeactivatedUpdate', 'OnOperationCompleted'] 71 72 def __init__(self, **kwargs): 73 super(RecommendationCard, self).__init__(**kwargs) 74 self._recommendationController = None 75 self.updateThread = None 76 77 def ApplyAttributes(self, attributes): 78 super(RecommendationCard, self).ApplyAttributes(attributes) 79 self.slotIdx = attributes.slotIdx 80 shouldBlinkBtn = attributes.shouldBlinkBtn 81 self.ConstructLayout() 82 if shouldBlinkBtn: 83 uthread2.call_after_wallclocktime_delay(self.BlinkButton, 0.8) 84 sm.RegisterNotify(self) 85 86 def OnOperationDeactivatedUpdate(self): 87 self.UpdateCard() 88 89 def OnOperationCompleted(self, category_id, operation_id): 90 if self.recommendationController and self.recommendationController.operationID == operation_id: 91 self.UpdateCard() 92 93 def ConstructLayout(self): 94 self.mainContainer = Container(name='mainContainer', parent=self, align=uiconst.CENTER, width=self.default_width, height=self.default_height) 95 Frame(bgParent=self.mainContainer, color=(1, 1, 1, 0.1)) 96 self.innerCard = Container(name='innerCard', parent=self.mainContainer, padding=1) 97 self.backgroundTexture = Sprite(name='backgroundTexture', parent=self.mainContainer, align=uiconst.TOALL, padding=1, opacity=0.7) 98 Fill(name='bgFill', parent=self.mainContainer, align=uiconst.TOALL, padding=1, color=(0, 0, 0, 1)) 99 self.topSection = TopSection(parent=self.innerCard) 100 self.dismissButton = Button(name='dismissButton', parent=self.innerCard, align=uiconst.TOPRIGHT, texturePath='res:/UI/Texture/classes/Treatments/recycleIcon.png', iconSize=18, func=self.DismissRecommendation) 101 Sprite(name='underlay', parent=self.innerCard, align=uiconst.TOPRIGHT, pos=(0, 0, 32, 32), texturePath='res:/UI/Texture/classes/Treatments/recycleButtonUnderlay.png', opacity=0.35 * DECO_OPACITY) 102 self.actionBtn = Button(name='actionBtn', parent=self.innerCard, align=uiconst.CENTERBOTTOM, label=GetByLabel('UI/Agents/Dialogue/Buttons/AcceptMission'), top=8) 103 self.textCont = TextCont(parent=self.innerCard, align=uiconst.TOBOTTOM) 104 self.loadingWheel = LoadingWheel(name='loadingWheel', parent=self, align=uiconst.CENTER) 105 self.loadingWheel.Hide() 106 self.refreshButton = Button(name='refreshButton', parent=self, align=uiconst.CENTER, label=GetByLabel('UI/Commands/Refresh'), top=8, func=self.RequestRecommendation) 107 self.refreshButton.Hide() 108 109 def BlinkButton(self, numBlinks = 1): 110 self.actionBtn.Blink(1, numBlinks) 111 112 def QuitRecommendation(self, *args): 113 if not self.recommendationController: 114 return 115 if not self.QuittingConfirmed(): 116 return 117 self.AnimateClose() 118 self.recommendationController.QuitRecommendation() 119 120 @staticmethod 121 def QuittingConfirmed(): 122 message = GetByLabel('UI/recommendations/QuitConfirmation') 123 answer = prompt_player_fsd_dialog('AskAreYouSure', {'cons': message}, uiconst.YESNO) 124 if answer == uiconst.ID_YES: 125 return True 126 return False 127 128 def RequestRecommendation(self, *args): 129 self.refreshButton.Hide() 130 self.recommendationController.RequestRecommendations(1, allowOld=False) 131 self.StartWaitingForUpdate() 132 133 def DismissRecommendation(self, *args): 134 if not self.recommendationController: 135 return 136 operationState = self.recommendationController.GetOperationState() 137 if operationState == CURRENT_ACTIVE and not self.QuittingConfirmed(): 138 return 139 self.AnimateClose() 140 self.recommendationController.DismissRecommendation() 141 142 def AnimateClose(self): 143 animations.MorphScalar(self.mainContainer, 'opacity', startVal=self.opacity, endVal=0.0, duration=ANIM_DURATION, callback=self.StartWaitingForUpdate) 144 145 def AnimateOpen(self): 146 self.loadingWheel.Hide() 147 animations.MorphScalar(self.mainContainer, 'opacity', startVal=self.mainContainer.opacity, endVal=1.0, duration=ANIM_DURATION, timeOffset=ANIM_DURATION, callback=self.mainContainer.Enable) 148 149 def StartWaitingForUpdate(self): 150 self.loadingWheel.Show() 151 self.mainContainer.Disable() 152 self.updateThread = uthread2.StartTasklet(self._WaitForUpdate) 153 154 def _WaitForUpdate(self): 155 secondsWaited = 0 156 secondsToWait = UPDATE_TIMEOUT_SECONDS 157 while secondsWaited < secondsToWait: 158 uthread2.Sleep(1.0) 159 secondsWaited += 1 160 161 self.refreshButton.Show() 162 self.loadingWheel.Hide() 163 164 def OnNewRecommendationReceived(self): 165 uthread2.StartTasklet(self.ProcessUpdatedData) 166 167 def ProcessUpdatedData(self): 168 if self.mainContainer.HasAnimation('opacity'): 169 uthread2.Sleep(ANIM_DURATION) 170 self.refreshButton.Hide() 171 self.StopWaitingForData() 172 self.LoadCard() 173 self.AnimateOpen() 174 175 def StopWaitingForData(self): 176 if not self.updateThread: 177 return 178 self.updateThread.kill() 179 self.updateThread = None 180 181 def LoadCard(self): 182 if not self.recommendationController or not self.recommendationController.operationID: 183 self.AnimateClose() 184 return 185 self.topSection.LoadSection(self.recommendationController) 186 backgroundTexturePath = self.recommendationController.GetBackgroundPath() 187 self.backgroundTexture.texturePath = backgroundTexturePath 188 self.textCont.LoadText(self.recommendationController) 189 self.UpdateCard() 190 self.recommendationController.RecommendationDisplayed() 191 192 @property 193 def recommendationController(self): 194 return self._recommendationController 195 196 @recommendationController.setter 197 def recommendationController(self, newController): 198 if self._recommendationController: 199 return 200 self._recommendationController = newController 201 self.recommendationController.onNewRecommendation.clear() 202 self._recommendationController.onNewRecommendation.connect(self.OnNewRecommendationReceived) 203 204 def UpdateCard(self): 205 if self.recommendationController.operationID is None: 206 return 207 operationState = self.recommendationController.GetOperationState() 208 self.topSection.UpdateSection() 209 self.UpdateButton(operationState) 210 if operationState == COMPLETED: 211 if self.recommendationController and self.recommendationController.IsCompletedAndUnseen(): 212 self.BlinkButton(3) 213 uthread2.call_after_wallclocktime_delay(self.ClearSlot, 3.5, self.slotIdx, self.recommendationController.operationID) 214 self.recommendationController.MarkCompletionAnimationAsSeen() 215 sm.ScatterEvent('OnRecommendationsUpdated') 216 217 def ClearSlot(self, slotIDx, operationID): 218 if not self.recommendationController: 219 return 220 if slotIDx != self.recommendationController.slotIdx: 221 return 222 if operationID != self.recommendationController.operationID: 223 return 224 self.recommendationController.ResetController() 225 self.AnimateClose() 226 self.RequestRecommendation() 227 228 def UpdateButton(self, operationState): 229 self.actionBtn.hint = '' 230 self.dismissButton.hint = GetByLabel('UI/recommendations/DismissConfirmation') 231 if operationState == COMPLETED: 232 self.actionBtn.SetLabel(GetByLabel('UI/Market/Orders/StatusCompleted')) 233 self.actionBtn.Disable() 234 self.actionBtn.func = None 235 self.actionBtn.style = ButtonStyle.SUCCESS 236 self.dismissButton.hint = GetByLabel('UI/recommendations/GetNewOpportunityHint') 237 if operationState == CURRENT_ACTIVE: 238 self.actionBtn.SetLabel(GetByLabel('UI/Commands/CmdQuit')) 239 self.actionBtn.Enable() 240 self.actionBtn.func = self.QuitRecommendation 241 self.actionBtn.style = ButtonStyle.WARNING 242 elif operationState == ANOTHER_ACTIVE: 243 self.actionBtn.SetLabel(GetByLabel('UI/Agents/Dialogue/Buttons/AcceptMission')) 244 self.actionBtn.hint = GetByLabel('UI/recommendations/AlreadyActive') 245 self.actionBtn.Disable() 246 self.actionBtn.func = None 247 self.actionBtn.style = ButtonStyle.NORMAL 248 elif operationState == NO_ACTIVE: 249 self.actionBtn.SetLabel(GetByLabel('UI/Agents/Dialogue/Buttons/AcceptMission')) 250 self.actionBtn.Enable() 251 self.actionBtn.func = self.recommendationController.AcceptRecommendation 252 self.actionBtn.style = ButtonStyle.NORMAL 253 else: 254 self.actionBtn.style = ButtonStyle.NORMAL 255 self.dismissButton.Enable() 256 257 def OnMouseEnter(self, *args): 258 if self.innerCard.HasAnimation('opacity'): 259 return 260 PlaySound(Sounds.HOVER) 261 animations.FadeTo(self.backgroundTexture, self.backgroundTexture.opacity, 1.0, 0.2, curveType=uiconst.ANIM_OVERSHOT) 262 263 def OnMouseExit(self, *args): 264 if self.innerCard.HasAnimation('opacity'): 265 return 266 animations.FadeTo(self.backgroundTexture, self.backgroundTexture.opacity, 0.7, 0.2) 267 268 269 class TopSection(Container): 270 default_align = uiconst.TOTOP 271 default_height = 195 272 273 def ApplyAttributes(self, attributes): 274 self.treatmentController = None 275 Container.ApplyAttributes(self, attributes) 276 StretchSpriteHorizontal(name='contentCardTop', parent=self, align=uiconst.TOTOP_NOPUSH, texturePath='res:/UI/Texture/classes/Treatments/topBar.png', height=14, leftEdgeSize=94, opacity=DECO_OPACITY, padRight=34) 277 self.statusLabel = EveLabelMedium(parent=self, align=uiconst.TOPLEFT, pos=(6, 6, 0, 0), text='') 278 self.checkmark = Sprite(name='checkmark', parent=self, align=uiconst.TOPLEFT, pos=(24, 15, 32, 32), texturePath='res:/UI/Texture/classes/Treatments/completedIcon.png', color=LIGHT_GREEN) 279 self.checkmark.display = False 280 self.moreIcon = DescriptionIcon(parent=self, align=uiconst.BOTTOMRIGHT, top=20, left=6) 281 iconCont = Container(name='iconCont', parent=self, align=uiconst.CENTERBOTTOM, pos=(0, 6, 122, 140)) 282 self.treatmentIcon = Sprite(name='treatmentIcon', parent=iconCont, align=uiconst.CENTER, pos=(0, 0, 120, 120)) 283 self.iconFrame = Sprite(name='iconFrame', parent=iconCont, align=uiconst.CENTER, pos=(0, 0, 122, 140), texturePath='res:/UI/Texture/classes/Treatments/iconFrame.png', opacity=DECO_OPACITY) 284 self.iconFill = Sprite(name='iconFill', parent=iconCont, align=uiconst.CENTER, pos=(0, 0, 120, 140), texturePath='res:/UI/Texture/classes/Treatments/iconFrameFilled.png', opacity=0.0) 285 Sprite(name='bottomBarLeft', parent=iconCont, align=uiconst.CENTERBOTTOM, pos=(-86, 10, 69, 14), texturePath='res:/UI/Texture/classes/Treatments/bottomBar.png', opacity=DECO_OPACITY) 286 Sprite(name='bottomBarRight', parent=iconCont, align=uiconst.CENTERBOTTOM, pos=(86, 10, -69, 14), texturePath='res:/UI/Texture/classes/Treatments/bottomBar.png', opacity=DECO_OPACITY) 287 288 def LoadSection(self, recommendationController): 289 self.treatmentController = recommendationController 290 self.treatmentIcon.texturePath = recommendationController.GetRecommendationIcon() 291 self.moreIcon.LoadTooltipPanel = self.LoadMoreTooltipPanel 292 self.UpdateSection() 293 294 def UpdateSection(self): 295 operationState = self.treatmentController.GetOperationState() 296 self.statusLabel.SetRGBA(*self.statusLabel.default_color) 297 self.iconFrame.opacity = DECO_OPACITY 298 self.iconFill.opacity = 0.0 299 solidIcon = False 300 self.statusLabel.text = '' 301 treatmentIconColor = Color.WHITE 302 self.checkmark.display = False 303 if operationState == REJECTED: 304 self.statusLabel.text = GetByLabel('UI/Contracts/ContractsWindow/Rejected') 305 elif operationState == CURRENT_ACTIVE: 306 self.statusLabel.text = GetByLabel('Achievements/UI/active') 307 self.statusLabel.SetRGBA(*MAIN_YELLOW) 308 iconFillColor = self.GetColorWithOpacity(MAIN_YELLOW, ICON_FILL_OPACITY) 309 self.iconFill.SetRGBA(*iconFillColor) 310 solidIcon = True 311 treatmentIconColor = LIGHT_YELLOW 312 self.iconFrame.opacity = 0.0 313 elif operationState == COMPLETED: 314 iconFillColor = self.GetColorWithOpacity(MAIN_GREEN, ICON_FILL_OPACITY) 315 self.iconFill.SetRGBA(*iconFillColor) 316 solidIcon = True 317 treatmentIconColor = LIGHT_GREEN 318 self.iconFrame.opacity = 0.0 319 self.checkmark.display = True 320 self.treatmentIcon.texturePath = self.treatmentController.GetRecommendationIcon(solidIcon) 321 self.treatmentIcon.SetRGBA(*treatmentIconColor) 322 323 def GetColorWithOpacity(self, color, opacity): 324 return color[:3] + (opacity,) 325 326 def LoadMoreTooltipPanel(self, tooltipPanel, *args): 327 hint = self.treatmentController.GetRecommendationHint() 328 if hint: 329 tooltipPanel.LoadGeneric1ColumnTemplate() 330 tooltipPanel.AddLabelMedium(text=hint, wrapWidth=200) 331 agencyContentGroupID = self.treatmentController.GetAgencyContentGroup() 332 if agencyContentGroupID: 333 if hint: 334 tooltipPanel.AddSpacer(height=6) 335 tooltipPanel.SetState(uiconst.UI_NORMAL) 336 self.showInAgencyBtn = Button(parent=tooltipPanel, align=uiconst.NOALIGN, label=GetByLabel('UI/Agency/ShowInAgency'), hint=GetByLabel('UI/Agency/ShowInAgencyHint'), texturePath='res:/UI/Texture/WindowIcons/theAgency.png', func=lambda *args: self.OpenInAgency(agencyContentGroupID)) 337 338 def OpenInAgency(self, agencyContentGroupID): 339 AgencyWndNew.OpenAndShowContentGroup(agencyContentGroupID) 340 sm.ScatterEvent('OnTreatmentWndOpenedOtherWnd', 'Agency') 341 342 343 class TextCont(Container): 344 default_name = 'treatmentContTextCont' 345 default_bgColor = (0, 0, 0, 0.5) 346 default_height = 130 347 348 def ApplyAttributes(self, attributes): 349 Container.ApplyAttributes(self, attributes) 350 self.headerLabel = Label(name='header', parent=self, align=uiconst.TOTOP, fontsize=18, top=5, padding=(10, 0, 10, 0)) 351 self.textLabelScrollingCont = ScrollingTextCont(parent=self, height=self.height, fontsize=13, labelPadding=(10, 0, 10, 0)) 352 353 def LoadText(self, treatmentController): 354 header = treatmentController.GetOperationName() 355 text = treatmentController.GetOperationDescription() 356 self.headerLabel.text = '<center>%s</center>' % header 357 width = RecommendationCard.default_width - self.headerLabel.padLeft - self.headerLabel.padRight 358 w, h = self.headerLabel.MeasureTextSize(header, width=width, fontsize=self.headerLabel.fontsize) 359 scrollingHeight = self.height - h - self.headerLabel.top - 50 360 self.textLabelScrollingCont.UpdateHeight(scrollingHeight) 361 self.textLabelScrollingCont.SetText('<center>%s</center>' % text) 362 363 364 SEC_PER_PIXEL = 0.07 365 366 class ScrollingTextCont(Container): 367 default_align = uiconst.TOTOP_NOPUSH 368 default_state = uiconst.UI_NORMAL 369 default_name = 'labelCont' 370 default_clipChildren = True 371 default_fontsize = fontconst.EVE_MEDIUM_FONTSIZE 372 fadeHeight = 14 373 374 def ApplyAttributes(self, attributes): 375 Container.ApplyAttributes(self, attributes) 376 text = attributes.text 377 labelPadding = attributes.labelPadding or 0 378 fontsize = attributes.fontsize or self.default_fontsize 379 self.label = Label(name='scrollContLabel', parent=self, align=uiconst.TOTOP_NOPUSH, text='', fontsize=fontsize, padding=labelPadding) 380 self.SetText(text) 381 382 def SetText(self, text): 383 self.label.text = text 384 self.label.SetBottomAlphaFade(self.height, maxFadeHeight=self.fadeHeight) 385 386 def UpdateHeight(self, newHeight): 387 self.height = newHeight 388 389 def OnMouseEnter(self, *args): 390 diff = self.label.textheight - self.height 391 if diff <= 2: 392 return 393 duration = mathext.clamp(SEC_PER_PIXEL * diff, 0, 2.0) 394 animations.MorphScalar(self.label, 'padTop', self.label.padTop, -diff, duration=duration) 395 fadeEndValue = (self.label.textheight, self.fadeHeight) 396 self.label.SetBottomAlphaFade(*fadeEndValue) 397 398 def OnMouseExit(self, *args): 399 self.label.StopAnimations() 400 self.label.padTop = 0 401 self.label.SetBottomAlphaFade(self.height, self.fadeHeight)