aurumStoreContainer.py (27233B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\view\aurumstore\aurumStoreContainer.py 2 import collections 3 import logging 4 import math 5 import carbonui.const as uiconst 6 import eve.client.script.ui.shared.pointerTool.pointerToolConst as pConst 7 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText 8 from carbonui.primitives.container import Container 9 from carbonui.primitives.containerAutoSize import ContainerAutoSize 10 from carbonui.primitives.fill import Fill 11 from carbonui.primitives.flowcontainer import FlowContainer 12 from carbonui.primitives.gradientSprite import GradientSprite 13 from carbonui.primitives.gridcontainer import GridContainer 14 from carbonui.primitives.sprite import Sprite 15 from carbonui.uicore import uicore 16 from eve.client.script.ui.shared.redeem.redeemPanel import GetOldRedeemPanel 17 from vgs.client.currency import OFFER_CURRENCY_SORT_ORDER 18 from eve.common.lib.vgsConst import CATEGORYTAG_GEM 19 from eve.client.script.ui.plex.textures import PLEX_32_SOLID_YELLOW 20 from eve.client.script.ui.util.focusUtil import postponeUntilFocus 21 from eve.client.script.ui.util.uiComponents import RunThreadOnce 22 from eve.client.script.ui.view.aurumstore.vgsOffer import Ribbon 23 from eve.client.script.ui.view.aurumstore.vgsOfferFilterBar import VgsOfferFilterBar 24 from eve.client.script.ui.view.aurumstore.vgsOfferGrid import OfferGrid 25 from eve.client.script.ui.view.aurumstore.vgsOfferScrollContainer import OfferScrollContainer 26 from eve.client.script.ui.view.aurumstore.vgsUiConst import BACKGROUND_COLOR, CONTENT_PADDING, MAX_CONTENT_WIDTH, REDEEM_BUTTON_BACKGROUND_COLOR, REDEEM_BUTTON_FILL_COLOR 27 from eve.client.script.ui.view.aurumstore.vgsUiPrimitives import AurLabelHeader, CategoryButton, CATEGORY_COLOR, ExitButton, GemLabelHeader, LogoHomeButton, SubCategoryButton, TAG_COLOR, VgsFilterCombo 28 from eve.client.script.ui.view.viewStateConst import ViewState 29 from fastcheckout.client.purchasepanels.purchaseButton import PurchaseButton 30 from fastcheckout.const import FROM_NES_HEADER 31 from localization import GetByLabel 32 from localization.util import Sort 33 logger = logging.getLogger(__name__) 34 HEADER_HEIGHT = 100 35 HEADER_PADDING = 2 36 CATEGORIES_HEIGHT = 36 37 AD_HEIGHT = 256 38 CAPTION_HEIGHT = 34 39 CAPTION_OFFSET = 30 40 SEARCH_BOX_WIDTH = 300 41 CONTENT_SLIP_UNDER_AREA_OPACITY = 0.8 42 BG_WIDTH = 2117 43 BG_HEIGHT = 1200 44 BUY_CURRENCY_BUTTON_WIDTH = 106 45 BUY_CURRENCY_BUTTON_HEIGHT = 30 46 BUY_CURRENCY_BUTTON_LEFT = 6 47 BUY_CURRENCY_BUTTON_FONTSIZE = 18 48 BUY_CURRENCY_FONTSIZE = 27 49 BUY_CURRENCY_ICONSIZE = 32 50 BUY_CURRENCY_TEXT_MARGIN = 10 51 BUY_CURRENCY_ICON_MARGIN = 8 52 SORT_PRICE_ASCENDING = 1 53 SORT_PRICE_DESCENDING = 2 54 SORT_NAME_ASCENDING = 3 55 SORT_NAME_DESCENDING = 4 56 DEFAULT_SORT_SELECTION = SORT_NAME_ASCENDING 57 PAGE_HOME = 1 58 PAGE_CATEGORY = 2 59 PAGE_SUBCATEGORY = 3 60 PAGE_SEARCH = 4 61 ROOT_CATEGORY_SHIP_SKINS = 1 62 ROOT_CATEGORY_APPAREL = 3 63 ROOT_CATEGORY_SERVICES = 21 64 THREAD_KEY_LOAD_PAGE = 'VGS.LoadPage' 65 KEY_OFFER = 'offer' 66 KEY_OFFERS = 'offers' 67 KEY_HAS_QUANTITY = 'hasQuantity' 68 KEY_QUANTITY = 'quantity' 69 ProductOffer = collections.namedtuple('ProductOffer', ['url', 70 'title', 71 'image', 72 'cost']) 73 SubCategory = collections.namedtuple('SubCategory', ['id', 'title']) 74 75 def SortByPrice(offers, ascending): 76 77 def get_sort_order_for_price(offer): 78 prices = [] 79 for pricing in offer.offerPricings: 80 sort_order = OFFER_CURRENCY_SORT_ORDER.get(pricing.currency, 0) 81 prices.append((sort_order, pricing.price)) 82 83 return max(prices) 84 85 return sorted(offers, key=get_sort_order_for_price, reverse=not ascending) 86 87 88 def SortByName(offers, ascending): 89 numericGroupData, nameGroupData = GetOffersGroupData(offers) 90 sortedNumericGroupData = sorted(numericGroupData.keys()) 91 sortedNameGroupData = sorted(nameGroupData.keys()) 92 allSortedOffers = [] 93 for groupName in sortedNumericGroupData: 94 offerData = [] 95 for offer in numericGroupData[groupName][KEY_OFFERS]: 96 isBundle = len(offer.productQuantities) > 1 97 for entry in offer.productQuantities.values(): 98 offerData.append({KEY_OFFER: offer, 99 KEY_QUANTITY: 1 if isBundle else entry.quantity}) 100 break 101 102 sortedOfferData = sorted(offerData, key=lambda o: o[KEY_QUANTITY]) 103 for data in sortedOfferData: 104 allSortedOffers.append(data[KEY_OFFER]) 105 106 for groupName in sortedNameGroupData: 107 sortedOfferData = sorted(nameGroupData[groupName][KEY_OFFERS], key=lambda o: o.name) 108 for data in sortedOfferData: 109 allSortedOffers.append(data) 110 111 if not ascending: 112 allSortedOffers.reverse() 113 return allSortedOffers 114 115 116 def GetOffersGroupData(offers): 117 PRODUCT_IDS = [2113, 118 4513, 119 2459, 120 1600, 121 3307] 122 numericGroupData = {} 123 nameGroupData = {} 124 for offer in offers: 125 offerName = offer.name 126 hasQuantity = False 127 for productID in offer.productQuantities.keys(): 128 if productID in PRODUCT_IDS: 129 offerName = str(PRODUCT_IDS.index(productID)) 130 hasQuantity = True 131 break 132 133 if hasQuantity: 134 if offerName in numericGroupData: 135 numericGroupData.get(offerName)[KEY_OFFERS].append(offer) 136 else: 137 numericGroupData[offerName] = {KEY_OFFERS: [offer], 138 KEY_HAS_QUANTITY: hasQuantity} 139 elif offerName in nameGroupData: 140 nameGroupData.get(offerName)[KEY_OFFERS].append(offer) 141 else: 142 nameGroupData[offerName] = {KEY_OFFERS: [offer], 143 KEY_HAS_QUANTITY: hasQuantity} 144 145 return (numericGroupData, nameGroupData) 146 147 148 def GetSortOrder(): 149 return settings.user.ui.Get('VgsOfferSortOrder', DEFAULT_SORT_SELECTION) 150 151 152 def SortOffers(offers): 153 offerOrder = GetSortOrder() 154 sortedOffers = offers 155 if offerOrder == SORT_PRICE_DESCENDING: 156 sortedOffers = SortByPrice(offers, False) 157 elif offerOrder == SORT_PRICE_ASCENDING: 158 sortedOffers = SortByPrice(offers, True) 159 elif offerOrder == SORT_NAME_DESCENDING: 160 sortedOffers = SortByName(offers, False) 161 elif offerOrder == SORT_NAME_ASCENDING: 162 sortedOffers = SortByName(offers, True) 163 return sortedOffers 164 165 166 class Tag: 167 168 def __init__(self, tagId, name): 169 self.id = tagId 170 self.name = name 171 172 173 class AurumStoreContainer(Container): 174 default_name = 'AurumStoreContainer' 175 default_state = uiconst.UI_NORMAL 176 177 def ApplyAttributes(self, attributes): 178 self.is_ready = False 179 Container.ApplyAttributes(self, attributes) 180 self.store = attributes.store 181 self.tagsByCategoryId = {} 182 self.selectedRootCategoryId = None 183 self.selectedCategoryId = None 184 self.selectedCurrency = None 185 self.activeTagsByRootCategoryId = {} 186 self.aurumStoreWindow = None 187 self.page = None 188 self.category = None 189 self.subCategory = None 190 self.subcategoryButtons = [] 191 self.fastCheckoutService = sm.GetService('fastCheckoutClientService') 192 self.vgsService = sm.GetService('vgsService') 193 self.viewStateService = sm.GetService('viewState') 194 self.featuredOfferIDs = self.vgsService.GetFeaturedOffers() 195 Fill(bgParent=self, color=(0.0, 0.0, 0.0, 1.0)) 196 self.CreateRedeemingPanelLayout() 197 self.CreateBaseLayout() 198 self.CreateHeaderLayout() 199 self.CreateContentLayout() 200 self.SetFilterOptions() 201 self.currentPlexBalance = 0 202 self.is_ready = True 203 self._OnResize() 204 205 def _OnResize(self, *args): 206 if not self.is_ready: 207 return 208 self.PositionBackgroundTexture() 209 top, left, width, height = self.GetAbsolute() 210 contentWidth = min(MAX_CONTENT_WIDTH, width) 211 self.leftContainer.width = (width - contentWidth) / 2 212 self.contentContainer.width = contentWidth 213 self.rightContainer.width = width - self.leftContainer.width - contentWidth 214 self.redeemingPanel.width = width 215 self.SetSidebarContentMask() 216 217 def CreateHeaderLayout(self): 218 self._AddPlex() 219 LogoHomeButton(parent=self.headerContainer, align=uiconst.TOPLEFT, onClick=self.OnClickHomeButton) 220 ExitButton(parent=self.headerContainer, align=uiconst.TOPRIGHT, onClick=lambda : self.exit_vgs(), top=4, left=10, hint=GetByLabel('UI/VirtualGoodsStore/ExitStore')) 221 222 def _AddPlex(self): 223 plexContainer = Container(name='PlexContainer', parent=self.headerContainer, align=uiconst.TOTOP, height=CAPTION_HEIGHT, top=CAPTION_OFFSET) 224 buttonContainer = ContainerAutoSize(name='BuyPlexButtonContainer', parent=plexContainer, align=uiconst.TORIGHT, width=BUY_CURRENCY_BUTTON_WIDTH + BUY_CURRENCY_BUTTON_LEFT, left=BUY_CURRENCY_BUTTON_LEFT) 225 self.buyPlexBtn = PurchaseButton(name='NES_BuyPlexButton', parent=buttonContainer, align=uiconst.CENTERRIGHT, width=BUY_CURRENCY_BUTTON_WIDTH, height=BUY_CURRENCY_BUTTON_HEIGHT, text=GetByLabel('UI/VirtualGoodsStore/BuyAurOnline'), fontsize=BUY_CURRENCY_BUTTON_FONTSIZE, func=self._BuyPlex) 226 self.aurLabel = AurLabelHeader(name=pConst.UNIQUE_NAME_NEW_EDEN_STORE_PLEX_AMOUNT, parent=plexContainer, align=uiconst.TORIGHT, amount=100, padRight=BUY_CURRENCY_TEXT_MARGIN, shouldUseUnits=False, amountSize=BUY_CURRENCY_FONTSIZE, uniqueUiName=pConst.UNIQUE_NAME_NEW_EDEN_STORE_PLEX_AMOUNT) 227 iconContainer = Container(name='BuyPlexIconContainer', parent=plexContainer, align=uiconst.TORIGHT, width=BUY_CURRENCY_ICONSIZE) 228 Sprite(name='PlexIcon', parent=iconContainer, texturePath=PLEX_32_SOLID_YELLOW, width=BUY_CURRENCY_ICONSIZE, height=BUY_CURRENCY_ICONSIZE, align=uiconst.CENTER) 229 if self.fastCheckoutService.is_china_funnel(): 230 plexContainer.top = 2 231 232 def exit_vgs(self): 233 if self.aurumStoreWindow: 234 self.aurumStoreWindow.CloseIfOpen() 235 uicore.cmd.ToggleAurumStore() 236 237 def _BuyPlex(self): 238 uicore.cmd.CmdBuyPlex(logContext=FROM_NES_HEADER) 239 240 def _BuyGem(self): 241 self.SelectCategoryByCategoryTag(CATEGORYTAG_GEM) 242 243 def SetSidebarContentMask(self): 244 for container in (self.leftContainer, self.rightContainer): 245 container.Flush() 246 GradientSprite(name='OfferSlipGradient', align=uiconst.TOTOP, parent=container, rgbData=((0.0, BACKGROUND_COLOR[:3]),), alphaData=((0.0, CONTENT_SLIP_UNDER_AREA_OPACITY), (0.5, 1.0), (1.0, 1.0)), height=self.topContainer.height + self.filterContainer.height + HEADER_PADDING, rotation=math.pi / 2) 247 248 def CreateBaseLayout(self): 249 self.wholeWidthContainer = Container(parent=self, name='WholeWindowContainer', align=uiconst.TOALL) 250 self.CreateBackgroundTexture() 251 self.leftContainer = Container(parent=self.wholeWidthContainer, name='LeftSideBar', align=uiconst.TOLEFT, opacity=0.0) 252 self.rightContainer = Container(parent=self.wholeWidthContainer, name='RightSideBar', align=uiconst.TORIGHT, opacity=0.0) 253 self.contentContainer = Container(parent=self.wholeWidthContainer, name='Content', align=uiconst.TOLEFT) 254 self.topContainer = ContainerAutoSize(name='Top Container', parent=self.contentContainer, align=uiconst.TOTOP) 255 Fill(name='SlipUnderLayer', bgParent=self.topContainer, color=BACKGROUND_COLOR) 256 self.headerContainer = Container(parent=self.topContainer, name='Header', align=uiconst.TOTOP, bgColor=(0.0, 0.0, 0.0, 1.0), height=HEADER_HEIGHT, clipChildren=True) 257 self.categoryContainer = Container(parent=self.topContainer, name='Categories', align=uiconst.TOTOP, height=CATEGORIES_HEIGHT, padTop=HEADER_PADDING, state=uiconst.UI_PICKCHILDREN) 258 self.subCategoryContainer = Container(name='SubCategories', parent=self.topContainer, align=uiconst.TOTOP, padTop=HEADER_PADDING, bgColor=TAG_COLOR, state=uiconst.UI_PICKCHILDREN, clipChildren=True) 259 self.subCategoryButtonContainer = FlowContainer(name='SubCategoryButtons', parent=self.subCategoryContainer, centerContent=True, align=uiconst.TOTOP, contentSpacing=(1, 0), state=uiconst.UI_PICKCHILDREN) 260 self.filterContainer = Container(name='Filter', parent=self.contentContainer, align=uiconst.TOTOP, padTop=HEADER_PADDING, state=uiconst.UI_PICKCHILDREN, height=CATEGORIES_HEIGHT) 261 262 def CreateBackgroundTexture(self): 263 self.nesBackground = Sprite(parent=self, name='NESBackground', texturePath='res:/UI/Texture/Vgs/background.png', align=uiconst.CENTER, state=uiconst.UI_DISABLED) 264 self.PositionBackgroundTexture() 265 266 def PositionBackgroundTexture(self): 267 clientHeight = uicore.desktop.height 268 percentOfClientHeight = float(clientHeight) / BG_HEIGHT 269 if clientHeight < BG_HEIGHT: 270 newHeight = BG_HEIGHT 271 newWidth = BG_WIDTH 272 else: 273 newHeight = clientHeight 274 newWidth = int(percentOfClientHeight * BG_WIDTH) 275 self.nesBackground.pos = (0, 276 0, 277 newWidth, 278 newHeight) 279 280 def CreateContentLayout(self): 281 self.contentScroll = OfferScrollContainer(parent=self.contentContainer, align=uiconst.TOALL) 282 self.grid = OfferGrid(parent=self.contentScroll, align=uiconst.TOTOP, contentSpacing=(CONTENT_PADDING, CONTENT_PADDING), padBottom=CONTENT_PADDING) 283 self.contentScroll.RegisterContentLoader(self.grid) 284 285 def CreateRedeemingPanelLayout(self): 286 instructionText = '<url=localsvc:method=ShowRedeemUI>%s</url>' % (GetByLabel('UI/RedeemWindow/ClickToInitiateRedeeming'),) 287 redeemPanelClass = GetOldRedeemPanel() 288 self.redeemingPanel = redeemPanelClass(parent=self, align=uiconst.TOBOTTOM, dragEnabled=False, instructionText=instructionText, redeemButtonBackgroundColor=REDEEM_BUTTON_BACKGROUND_COLOR, redeemButtonFillColor=REDEEM_BUTTON_FILL_COLOR) 289 self.redeemingPanel.UpdateDisplay() 290 291 def _SelectCategory(self, categoryId): 292 self.selectedRootCategoryId = categoryId 293 for button in self.categoryButtons: 294 if button.isActive and button.categoryId != categoryId: 295 button.SetActive(False) 296 elif button.categoryId == categoryId: 297 button.SetActive(True) 298 299 def _SelectSubCategory(self, subcategoryId): 300 self.selectedCategoryId = subcategoryId 301 for button in self.subcategoryButtons: 302 if button.isActive and button.categoryId != subcategoryId: 303 button.SetActive(False) 304 elif button.categoryId == subcategoryId: 305 button.SetActive(True) 306 307 def OnClickCategory(self, categoryId): 308 self.LoadCategoryPage(categoryId) 309 310 @RunThreadOnce(THREAD_KEY_LOAD_PAGE) 311 def LoadCategoryPage(self, categoryId): 312 if self.page == PAGE_CATEGORY and self.selectedRootCategoryId == categoryId and self.selectedCategoryId is None: 313 return 314 logger.debug('Loading category page: %s', categoryId) 315 self.page = PAGE_CATEGORY 316 self._SelectCategory(categoryId) 317 self.ConstructSubCategoryButtons(categoryId) 318 self._SelectSubCategory(None) 319 self.SetOffersAndTags(categoryId) 320 self.category = categoryId 321 self.subCategory = None 322 self._NotifyStorePageLoaded() 323 324 def GetSubCategories(self, categoryId): 325 if categoryId is None: 326 return 327 categoriesById = self.store.GetCategories() 328 category = categoriesById[categoryId] 329 subcategories = [ categoriesById[subCatId] for subCatId in category.subcategories ] 330 subcategories = Sort(subcategories, key=lambda c: c.name) 331 return subcategories 332 333 def OnClickSubCategory(self, subcategoryId): 334 self.LoadSubCategoryPage(subcategoryId) 335 336 def SelectCategory(self, categoryId): 337 if categoryId in self.store.GetRootCategoryList(): 338 self.LoadCategoryPage(categoryId) 339 else: 340 self.LoadSubCategoryPage(categoryId) 341 342 def SelectCategoryByCategoryTag(self, categoryTag): 343 categoryId = self.store.GetCategoryIdByCategoryTag(categoryTag) 344 self.SelectCategory(categoryId) 345 346 @RunThreadOnce(THREAD_KEY_LOAD_PAGE) 347 def LoadSubCategoryPage(self, subcategoryId): 348 if self.page == PAGE_SUBCATEGORY and self.selectedCategoryId == subcategoryId: 349 return 350 logger.debug('Loading sub category page: %s', subcategoryId) 351 self.page = PAGE_SUBCATEGORY 352 self.selectedCategoryId = subcategoryId 353 self.SetOffersAndTags(subcategoryId) 354 rootCategoryId = self.store.GetCategories()[subcategoryId].parentId 355 self._ConstructSubCategoryButtons(rootCategoryId) 356 self._SelectCategory(rootCategoryId) 357 self._SelectSubCategory(subcategoryId) 358 self.subCategory = subcategoryId 359 self._NotifyStorePageLoaded() 360 361 def SetOffersAndTags(self, categoryId): 362 tags = self.store.GetTagsByCategoryId(categoryId) 363 self.SetFilterTags(tags) 364 tagIds = self.filterBar.GetSelectedFilterTagIds() 365 offers = self.store.GetFilteredOffers(categoryId, None, tagIds) 366 self.SetOffers(offers) 367 368 @RunThreadOnce(THREAD_KEY_LOAD_PAGE) 369 def OnClickHomeButton(self): 370 self.LoadLandingPage() 371 372 def LoadLandingPage(self): 373 logger.debug('LoadLandingPage') 374 if self.page == PAGE_HOME: 375 return 376 logger.debug('Loading landing page') 377 self.page = PAGE_HOME 378 self._ConstructSubCategoryButtons(None) 379 self._SelectCategory(None) 380 self._SelectSubCategory(None) 381 self.SetFilterTags([]) 382 offers = self.store.GetOffers().values() 383 self.SetOffers(offers) 384 self.category = None 385 self.subCategory = None 386 self._NotifyStorePageLoaded() 387 388 def __find_button_label(self, button_list, category_id): 389 for button in button_list: 390 if button.categoryId == category_id: 391 return button.name 392 393 def _NotifyStorePageLoaded(self): 394 page_name = None 395 if self.page == 1: 396 page_name = 'LogoHomeButton' 397 elif self.page == 2: 398 page_name = self.__find_button_label(self.categoryButtons, self.category) 399 elif self.page == 3: 400 page_name = self.__find_button_label(self.subcategoryButtons, self.subCategory) 401 sm.ScatterEvent('OnNESPageLoaded', page_name) 402 403 @postponeUntilFocus 404 def SetPlexBalance(self, balance, *args, **kwargs): 405 uicore.animations.MorphScalar(self, 'currentPlexBalance', startVal=float(self.currentPlexBalance), endVal=float(balance), curveType=uiconst.ANIM_SMOOTH, duration=1.5, callback=lambda : self.SetCurrentPlexBalance(balance)) 406 407 def SetCurrentPlexBalance(self, balance): 408 self._currentPlexBalance = balance 409 if not self.destroyed: 410 self.aurLabel.SetAmount(self._currentPlexBalance) 411 412 def GetCurrentPlexBalance(self): 413 return self._currentPlexBalance 414 415 currentPlexBalance = property(GetCurrentPlexBalance, SetCurrentPlexBalance) 416 417 def SetCategories(self, categories): 418 logger.debug('SetCategories %s', categories) 419 self.categoryContainer.Flush() 420 searchContainer = Container(name='SearchBox', parent=self.categoryContainer, width=SEARCH_BOX_WIDTH, align=uiconst.TORIGHT) 421 categoryButtonsContainer = GridContainer(name='ButtonGrid', parent=self.categoryContainer, align=uiconst.TOALL, columns=len(categories), lines=1) 422 tagById = self.store.GetTags() 423 self.categoryButtons = [] 424 for category in categories: 425 button = CategoryButton(parent=categoryButtonsContainer, categoryId=category.id, label=category.name, align=uiconst.TOALL, onClick=self.OnClickCategory, padRight=1, name='CategoryButton_{}'.format(category.id)) 426 self.categoryButtons.append(button) 427 tags = [] 428 for tagId in category.tagIds: 429 tag = tagById.get(tagId) 430 if tag: 431 tags.append(Tag(tag.id, tag.name)) 432 433 self.tagsByCategoryId[category.id] = tags 434 435 iconContainer = Container(name='SearchIconContainer', parent=searchContainer, width=CATEGORIES_HEIGHT, align=uiconst.TOLEFT, bgColor=CATEGORY_COLOR) 436 Sprite(parent=iconContainer, texturePath='res:/UI/Texture/Vgs/Search_icon.png', width=32, height=32, align=uiconst.CENTER) 437 self.searchEdit = SingleLineEditText(parent=searchContainer, align=uiconst.TORIGHT, pos=(0, 438 0, 439 SEARCH_BOX_WIDTH - CATEGORIES_HEIGHT - 2, 440 0), fontsize=16, padding=(1, 0, 0, 0), OnChange=self.LoadSearchPage, bgColor=TAG_COLOR) 441 self.searchEdit.ShowClearButton(icon='res:/UI/Texture/Icons/73_16_45.png') 442 self.searchEdit.SetHistoryVisibility(False) 443 self.searchEdit.underlay.Hide() 444 445 @RunThreadOnce(THREAD_KEY_LOAD_PAGE) 446 def LoadSearchPage(self, searchString): 447 if len(searchString) > 0: 448 self.page = PAGE_SEARCH 449 self._SelectSubCategory(None) 450 self._SelectCategory(None) 451 self._ConstructSubCategoryButtons(None) 452 self.SetFilterTags([]) 453 self.viewStateService.GetView(ViewState.VirtualGoodsStore).Search(searchString) 454 else: 455 self.LoadLandingPage() 456 457 @RunThreadOnce(THREAD_KEY_LOAD_PAGE) 458 def LoadTypeIdPage(self, typeIds): 459 self.page = PAGE_SEARCH 460 self._SelectSubCategory(None) 461 self._SelectCategory(None) 462 self._ConstructSubCategoryButtons(None) 463 self.SetFilterTags([]) 464 offers = self.store.SearchOffersByTypeIDs(typeIds) 465 if len(offers) == 1: 466 offer = offers[0] 467 categoryId = self.store.GetSubCategoryId(offer.categories) 468 self.vgsService.ShowOffer(offer.id, categoryId) 469 else: 470 self.SetOffers(offers) 471 472 @RunThreadOnce('VGS.SetSubCategories') 473 def ConstructSubCategoryButtons(self, categoryId): 474 self._ConstructSubCategoryButtons(categoryId) 475 476 def _ConstructSubCategoryButtons(self, categoryId): 477 subcategories = self.GetSubCategories(categoryId) 478 self.subCategoryButtonContainer.Flush() 479 self.subcategoryButtons = [] 480 if subcategories is None: 481 if self.subCategoryContainer.height > 0: 482 uicore.animations.MorphScalar(self.subCategoryContainer, attrName='height', startVal=self.subCategoryContainer.height, endVal=0, duration=0.5, callback=self.SetSidebarContentMask) 483 else: 484 if int(self.subCategoryContainer.height) != CATEGORIES_HEIGHT: 485 uicore.animations.MorphScalar(self.subCategoryContainer, attrName='height', startVal=self.subCategoryContainer.height, endVal=CATEGORIES_HEIGHT, duration=0.5, sleep=False, callback=self.SetSidebarContentMask) 486 for subcategory in subcategories: 487 button = SubCategoryButton(parent=self.subCategoryButtonContainer, label=subcategory.name, align=uiconst.NOALIGN, height=CATEGORIES_HEIGHT, categoryId=subcategory.id, onClick=self.OnClickSubCategory, name='SubCategoryButton_{}'.format(subcategory.id)) 488 self.subcategoryButtons.append(button) 489 490 def SetOffers(self, offers): 491 offersToDisplay = [] 492 featuredOffersToDisplay = [] 493 for offer in offers: 494 if not offer.canPurchase: 495 continue 496 if offer.id in self.featuredOfferIDs: 497 featuredOffersToDisplay.append(offer) 498 else: 499 offersToDisplay.append(offer) 500 501 regularOffers = [] 502 specialOffers = {} 503 for o in offersToDisplay: 504 if o.label is not None: 505 labelName = o.label.name 506 if labelName not in Ribbon.SORTING: 507 logger.error('Unknown VGS offer label name %s for offer ID %d' % (labelName, o.id)) 508 continue 509 if labelName not in specialOffers: 510 specialOffers[labelName] = [o] 511 else: 512 specialOffers[labelName].append(o) 513 else: 514 regularOffers.append(o) 515 516 orderedOffers = [] 517 if featuredOffersToDisplay: 518 orderedOffers.extend(SortOffers(featuredOffersToDisplay)) 519 for offerLabel in Ribbon.SORTING: 520 if offerLabel in specialOffers: 521 orderedOffers.extend(SortOffers(specialOffers[offerLabel])) 522 523 orderedOffers.extend(SortOffers(regularOffers)) 524 self.grid.SetOffers(orderedOffers, show_skinr_banner=self.should_show_skinr_banner) 525 526 def SetFilterOptions(self): 527 self.filterContainer.Flush() 528 Fill(name='SlipUnderLayer', bgParent=self.filterContainer, color=(0.25, 0.25, 0.25, 1.0), opacity=CONTENT_SLIP_UNDER_AREA_OPACITY, padTop=-HEADER_PADDING * 2) 529 options = [(GetByLabel('UI/VirtualGoodsStore/Sorting/ByPriceAscending'), SORT_PRICE_ASCENDING), 530 (GetByLabel('UI/VirtualGoodsStore/Sorting/ByPriceDescending'), SORT_PRICE_DESCENDING), 531 (GetByLabel('UI/VirtualGoodsStore/Sorting/ByNameAscending'), SORT_NAME_ASCENDING), 532 (GetByLabel('UI/VirtualGoodsStore/Sorting/ByNameDescending'), SORT_NAME_DESCENDING)] 533 self.filterCombo = VgsFilterCombo(parent=self.filterContainer, align=uiconst.TORIGHT, options=options, callback=self.OnSortOrderChanged, select=GetSortOrder(), padding=(4, 2, 0, 4)) 534 self.filterBar = VgsOfferFilterBar(parent=self.filterContainer, onFilterChanged=self.OnFilterChanged) 535 536 def SetFilterTags(self, tags): 537 activeTags = self.activeTagsByRootCategoryId.get(self.GetSelectedRootCategoryId(), {}) 538 self.subCategoryContainer.state = uiconst.UI_PICKCHILDREN 539 self.filterBar.SetTags(tags, activeTags) 540 541 def OnSortOrderChanged(self, combo, key, value): 542 settings.user.ui.Set('VgsOfferSortOrder', value) 543 self.viewStateService.GetView(ViewState.VirtualGoodsStore)._LogFilterChange(value) 544 self.SetOffers(self.grid.GetOffers()) 545 546 def OnFilterChanged(self): 547 tagIds = self.filterBar.GetSelectedFilterTagIds() 548 rootCategoryId = self.GetSelectedRootCategoryId() 549 self.activeTagsByRootCategoryId[rootCategoryId] = tagIds 550 filterCategory = self.selectedCategoryId or rootCategoryId 551 filterCurrency = self.selectedCurrency 552 offers = self.store.GetFilteredOffers(filterCategory, filterCurrency, tagIds) 553 self.SetOffers(offers) 554 555 def GetSelectedCategoryId(self): 556 return self.selectedCategoryId 557 558 def GetSelectedRootCategoryId(self): 559 return self.selectedRootCategoryId 560 561 def OnMouseWheel(self, dz, *args): 562 self.contentScroll.OnMouseWheel(dz) 563 564 def OnOfferPurchase(self, offerID): 565 if not self.store.GetOffer(offerID).canPurchase: 566 self.grid.RemoveOffer(offerID, show_skinr_banner=self.should_show_skinr_banner) 567 568 def SetFeaturedOffers(self, offerIDs): 569 self.featuredOfferIDs = offerIDs or [] 570 self.SetOffers(self.grid.GetOffers()) 571 572 @property 573 def should_show_skinr_banner(self): 574 if session.charid is None: 575 return False 576 elif self.page == PAGE_HOME: 577 return True 578 elif self.selectedRootCategoryId == ROOT_CATEGORY_SHIP_SKINS: 579 return True 580 else: 581 return False