costInfo.py (14727B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\cosmetics\ship\controls\costInfo.py 2 import eveformat 3 import eveicon 4 import uthread2 5 from carbonui import Align, PickState, TextColor, uiconst 6 from carbonui.text.styles import TextHeader, TextBody, TextDetail 7 from carbonui.control.button import Button, ButtonStyle 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.layoutGrid import LayoutGrid 12 from carbonui.primitives.sprite import Sprite 13 from cosmetics.client.ships import ship_skin_signals 14 from cosmetics.client.ships.skins.errors import NoTimeRemainingException 15 from cosmetics.client.ships.skins.live_data import current_skin_design, current_skin_design_signals 16 from cosmetics.client.shipSkinSequencingSvc import get_ship_skin_sequencing_svc 17 from cosmetics.common.ships.skins.util import Currency 18 from eve.client.script.ui import eveColor 19 from eve.client.script.ui.control.itemIcon import ItemIcon 20 from player_messaging.client.ui_message import message_player 21 from eve.client.script.ui.control.warningIcon import WarningIcon 22 from eve.client.script.ui.shared.neocom.wallet.buyMissingPlexDialog import BuyMissingPlexDialog 23 from eve.client.script.ui.shared.neocom.wallet.walletWnd import WalletWindow 24 from inventorycommon.const import typeLoyaltyPointsHeraldry 25 from localization import GetByLabel 26 from plex.client.api.repository import get_repository 27 from publicGateway.grpc.exceptions import GenericException 28 from stackless_response_router.exceptions import TimeoutException 29 30 class CostInfo(ContainerAutoSize): 31 default_display = False 32 LEFT_WIDTH = 40 33 SPACING = 4 34 35 def __init__(self, cost = None, currency = None, discount = None, *args, **kwargs): 36 super(CostInfo, self).__init__(*args, **kwargs) 37 self._cost = cost 38 self._discount = discount 39 self._currency = currency 40 self.construct_layout() 41 self.update() 42 43 def construct_layout(self): 44 self.construct_header() 45 self.construct_cost() 46 47 def construct_header(self): 48 header_container = Container(name='header_container', parent=self, align=Align.TOTOP, height=16) 49 left_container = Container(parent=header_container, align=Align.TOLEFT, width=self.LEFT_WIDTH) 50 Sprite(parent=left_container, texturePath='res:/UI/Texture/classes/Cosmetics/Ship/overlays/cost_info_corner.png', width=self.LEFT_WIDTH, height=16) 51 right_container = Container(parent=header_container, align=Align.TOALL, padLeft=self.SPACING) 52 Fill(parent=right_container, color=eveColor.WHITE, opacity=0.1) 53 TextDetail(parent=right_container, align=Align.CENTERLEFT, text=self.cost_header_text, color=TextColor.SECONDARY, padLeft=8) 54 55 def construct_cost(self): 56 cost_container = Container(name='cost_container', parent=self, align=Align.TOTOP, height=32, padTop=self.SPACING) 57 left_container = Container(parent=cost_container, align=Align.TOLEFT, width=self.LEFT_WIDTH) 58 Fill(parent=left_container, color=eveColor.WHITE, opacity=0.1) 59 self.construct_currency_icon(parent=left_container) 60 self.right_container = Container(name='rightContainer', parent=cost_container, align=Align.TOALL, padLeft=self.SPACING) 61 Fill(parent=self.right_container, color=eveColor.SMOKE_BLUE, opacity=0.2) 62 self.layout_grid = LayoutGrid(parent=self.right_container, align=Align.CENTERLEFT, left=8, cellSpacing=8) 63 self.cost_label = TextHeader(parent=self.layout_grid, align=Align.CENTERLEFT) 64 self.discount_label = TextBody(parent=self.layout_grid, align=Align.CENTERLEFT, color=eveColor.SUCCESS_GREEN) 65 warning_icon_container = Container(name='warning_icon_container', parent=self.right_container, align=Align.TOLEFT, width=16, padLeft=8) 66 self.warning_icon = WarningIcon(name='warning_icon', parent=warning_icon_container, align=Align.CENTER, display=False, top=1) 67 68 def construct_currency_icon(self, parent): 69 self.currency_icon = Sprite(parent=parent, align=Align.CENTER, state=uiconst.UI_DISABLED, width=16, height=16) 70 71 def update(self): 72 self.display = self.cost is not None 73 if self.cost is None: 74 return 75 if self.currency == Currency.PLEX: 76 self.currency_icon.texturePath = eveicon.plex 77 self.currency_icon.color = eveColor.PLEX_YELLOW 78 self.cost_label.text = eveformat.number(self.cost, decimal_places=0) 79 text_padding = 40 80 elif self.currency == Currency.ISK: 81 self.currency_icon.texturePath = eveicon.isk 82 self.currency_icon.color = eveColor.WHITE 83 self.cost_label.text = eveformat.number(self.cost) 84 text_padding = 16 85 self._update_discount_text() 86 if self.cost: 87 cost_header_text_width, _ = TextDetail.MeasureTextSize(self.cost_header_text) 88 cost_amount_text_width, _ = TextHeader.MeasureTextSize(self.cost) 89 discount_text_width, _ = TextBody.MeasureTextSize(self.discount_label.text) 90 longest_text_width = max(cost_header_text_width, cost_amount_text_width + discount_text_width) 91 desired_width = self.LEFT_WIDTH + self.SPACING + longest_text_width + text_padding 92 self.width = max(self.minWidth, desired_width) 93 94 def _update_discount_text(self): 95 if self._discount: 96 self.discount_label.text = GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/CostWithDiscount', discount_percentage=self._discount) 97 else: 98 self.discount_label.text = '' 99 100 @property 101 def cost_header_text(self): 102 return GetByLabel('UI/Common/Cost') 103 104 @property 105 def cost(self): 106 return self._cost 107 108 @cost.setter 109 def cost(self, value): 110 if self._cost == value: 111 return 112 self._cost = value 113 self.update() 114 115 @property 116 def currency(self): 117 return self._currency 118 119 @currency.setter 120 def currency(self, value): 121 if self._currency == value: 122 return 123 self._currency = value 124 self.update() 125 126 def set_cost(self, cost, discount = None): 127 self._cost = cost 128 self._discount = discount 129 self.update() 130 131 132 class SequencingJobCostInfo(CostInfo): 133 134 def __init__(self, sequencing_job = None, *args, **kwargs): 135 self._sequencing_job = sequencing_job 136 self._update_thread = None 137 super(SequencingJobCostInfo, self).__init__(currency=Currency.PLEX, *args, **kwargs) 138 139 def Close(self): 140 try: 141 self.kill_update_thread() 142 finally: 143 super(SequencingJobCostInfo, self).Close() 144 145 def kill_update_thread(self): 146 if self._update_thread is not None: 147 self._update_thread.kill() 148 self._update_thread = None 149 150 @property 151 def sequencing_job(self): 152 return self._sequencing_job 153 154 @sequencing_job.setter 155 def sequencing_job(self, value): 156 self._sequencing_job = value 157 self.update_sequencing_job() 158 159 def update_sequencing_job(self): 160 if self.sequencing_job: 161 self.kill_update_thread() 162 self._update_thread = uthread2.start_tasklet(self.update_sequencing_job_async) 163 else: 164 self.display = False 165 166 def update_sequencing_job_async(self): 167 try: 168 try: 169 plex_cost = get_ship_skin_sequencing_svc().get_early_completion_cost(self.sequencing_job.job_id) 170 except (GenericException, TimeoutException): 171 message_player(GetByLabel('UI/Common/CannotConnectToServer')) 172 plex_cost = None 173 174 self.cost = plex_cost 175 self.display = plex_cost is not None 176 except NoTimeRemainingException: 177 self.display = False 178 179 180 class StudioCostInfo(CostInfo): 181 182 def __init__(self, *args, **kwargs): 183 super(StudioCostInfo, self).__init__(currency=Currency.PLEX, *args, **kwargs) 184 self._update_thread = None 185 self.connect_signals() 186 self.update_cost_label() 187 188 def Close(self): 189 try: 190 self.kill_update_thread() 191 self.disconnect_signals() 192 finally: 193 super(StudioCostInfo, self).Close() 194 195 def kill_update_thread(self): 196 if self._update_thread: 197 self._update_thread.kill() 198 self._update_thread = None 199 200 def connect_signals(self): 201 current_skin_design_signals.on_design_reset.connect(self.on_design_reset) 202 current_skin_design_signals.on_existing_design_loaded.connect(self.on_existing_design_loaded) 203 current_skin_design_signals.on_ship_type_id_changed.connect(self.on_ship_type_id_changed) 204 current_skin_design_signals.on_slot_fitting_changed.connect(self.on_slot_fitting_changed) 205 ship_skin_signals.on_skin_sequencing_cache_invalidated.connect(self.on_skin_sequencing_cache_invalidated) 206 207 def disconnect_signals(self): 208 current_skin_design_signals.on_design_reset.disconnect(self.on_design_reset) 209 current_skin_design_signals.on_existing_design_loaded.disconnect(self.on_existing_design_loaded) 210 current_skin_design_signals.on_ship_type_id_changed.disconnect(self.on_ship_type_id_changed) 211 current_skin_design_signals.on_slot_fitting_changed.disconnect(self.on_slot_fitting_changed) 212 ship_skin_signals.on_skin_sequencing_cache_invalidated.disconnect(self.on_skin_sequencing_cache_invalidated) 213 214 def update_cost_label(self): 215 self.kill_update_thread() 216 self._update_thread = uthread2.start_tasklet(self.update_cost_label_async) 217 218 def update_cost_label_async(self): 219 try: 220 plex_cost, discount = get_ship_skin_sequencing_svc().get_sequencing_plex_price(design=current_skin_design.get(), nb_runs=1) 221 except (GenericException, TimeoutException): 222 message_player(GetByLabel('UI/Common/CannotConnectToServer')) 223 plex_cost = None 224 discount = None 225 226 self.set_cost(plex_cost, discount) 227 self.display = plex_cost is not None 228 229 def on_design_reset(self, *args): 230 self.update_cost_label() 231 232 def on_existing_design_loaded(self, *args): 233 self.update_cost_label() 234 235 def on_ship_type_id_changed(self, *args): 236 self.update_cost_label() 237 238 def on_slot_fitting_changed(self, *args): 239 self.update_cost_label() 240 241 def on_skin_sequencing_cache_invalidated(self, *args): 242 self.update_cost_label() 243 244 @property 245 def cost_header_text(self): 246 return GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/CostPreviewDescription') 247 248 249 class SequencingPanelCostInfo(CostInfo): 250 default_state = uiconst.UI_NORMAL 251 252 def update(self): 253 super(SequencingPanelCostInfo, self).update() 254 self.cost_label.rgba = eveColor.DANGER_RED if self.insufficient_balance else TextColor.HIGHLIGHT 255 self.warning_icon.display = self.insufficient_balance 256 self.layout_grid.left = 32 if self.insufficient_balance else 8 257 258 @property 259 def cost_header_text(self): 260 return GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/TotalPLEXCost') 261 262 @property 263 def plex_balance(self): 264 return get_repository().get_balance() 265 266 @property 267 def insufficient_balance(self): 268 return self.cost > self.plex_balance 269 270 def LoadTooltipPanel(self, tooltipPanel, *args): 271 tooltipPanel.LoadStandardSpacing() 272 tooltipPanel.columns = 1 273 tooltipPanel.pickState = PickState.ON 274 tooltipPanel.cellSpacing = (8, 16) 275 if self.insufficient_balance: 276 TextBody(parent=tooltipPanel, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/InsufficientPLEX', plex_balance=int(self.plex_balance) or 0), maxWidth=220, align=Align.CENTERLEFT) 277 Button(parent=tooltipPanel, label=GetByLabel('UI/Wallet/BuyMore'), func=self.on_buy_more_plex_button, style=ButtonStyle.MONETIZATION, align=Align.TOTOP) 278 else: 279 TextBody(parent=tooltipPanel, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/PLEXBalance', plex_balance=int(self.plex_balance) or 0), maxWidth=220, align=Align.CENTERLEFT) 280 281 def on_buy_more_plex_button(self, *args): 282 buy_missing_plex_dialog = BuyMissingPlexDialog(required_amount=self.cost) 283 buy_missing_plex_dialog.ShowModal() 284 285 def GetTooltipPointer(self): 286 return uiconst.POINT_RIGHT_2 287 288 289 class BuyersFeeCostInfo(CostInfo): 290 default_display = False 291 292 def __init__(self, cost, listing, *args, **kwargs): 293 self.listing = listing 294 self._buyer_fee_amount = None 295 super(BuyersFeeCostInfo, self).__init__(cost, *args, **kwargs) 296 297 def construct_header(self): 298 pass 299 300 def construct_layout(self): 301 super(BuyersFeeCostInfo, self).construct_layout() 302 self.right_container.hint = GetByLabel('UI/Personalization/ShipSkins/SKINR/BuyersFeeHint') 303 304 def construct_currency_icon(self, parent): 305 self.currency_icon = ItemIcon(name='currency_icon', parent=parent, align=uiconst.CENTER, width=20, height=20, typeID=typeLoyaltyPointsHeraldry) 306 307 def update(self): 308 self.display = self.buyer_fee_amount is not None 309 if self.buyer_fee_amount is None: 310 return 311 self.cost_label.text = eveformat.number(self.buyer_fee_amount) 312 evermark_balance = sm.GetService('loyaltyPointsWalletSvc').GetCharacterEvermarkBalance() 313 if self.listing.seller_id == session.charid: 314 show_warning = False 315 else: 316 show_warning = evermark_balance < self.buyer_fee_amount 317 if show_warning: 318 self.cost_label.color = TextColor.DANGER 319 self.warning_icon.display = True 320 self.state = uiconst.UI_NORMAL 321 self.hint = GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/ListingErrors/InsufficientFunds') 322 else: 323 self.cost_label.color = TextColor.NORMAL 324 self.warning_icon.display = False 325 self.state = uiconst.UI_PICKCHILDREN 326 self.hint = None 327 self.layout_grid.left = 32 if show_warning else 8 328 additional_padding = 40 if show_warning else 16 329 cost_amount_text_width, _ = TextHeader.MeasureTextSize(self.cost_label.text) 330 desired_width = self.LEFT_WIDTH + self.SPACING + cost_amount_text_width + additional_padding 331 self.width = max(self.minWidth, desired_width) 332 333 @property 334 def buyer_fee_amount(self): 335 return self._buyer_fee_amount 336 337 @buyer_fee_amount.setter 338 def buyer_fee_amount(self, value): 339 self._buyer_fee_amount = value 340 self.update()