NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

licenseSelector.py (14601B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\cosmetics\ship\pages\studio\licenseSelector.py
      2 import eveicon
      3 import uthread2
      4 from carbonui import Align, TextColor, uiconst
      5 from carbonui.text.styles import TextBody
      6 from carbonui.primitives.container import Container
      7 from carbonui.primitives.containerAutoSize import ContainerAutoSize
      8 from carbonui.primitives.sprite import Sprite
      9 from carbonui.primitives.stretchspritehorizontal import StretchSpriteHorizontal
     10 from carbonui.uianimations import animations
     11 from cosmetics.client.ships import ship_skin_signals
     12 from cosmetics.client.shipSkinComponentSvc import get_ship_skin_component_svc
     13 from cosmetics.client.ships.skins.live_data import current_skin_design_signals
     14 from cosmetics.common.ships.skins.live_data.component_license_type import ComponentLicenseType
     15 from eve.client.script.ui import eveColor
     16 from player_messaging.client.ui_message import message_player
     17 from eve.client.script.ui.cosmetics.ship.pages.sequence import sequence_ui_signals
     18 from localization import GetByLabel
     19 from publicGateway.grpc.exceptions import GenericException
     20 from stackless_response_router.exceptions import TimeoutException
     21 
     22 class LicenseSelector(ContainerAutoSize):
     23     default_height = 20
     24 
     25     def __init__(self, component_instance, *args, **kwargs):
     26         super(LicenseSelector, self).__init__(*args, **kwargs)
     27         self._component_instance = component_instance
     28         self.bound_license = None
     29         self.unbound_license = None
     30         self._update_thread = None
     31         self._update_component_license_granted_thread = None
     32         self.construct_layout()
     33         self.update_async()
     34         self.connect_signals()
     35 
     36     def Close(self):
     37         try:
     38             self.kill_threads()
     39             self.disconnect_signals()
     40         finally:
     41             super(LicenseSelector, self).Close()
     42 
     43     def kill_threads(self):
     44         self.kill_update_thread()
     45         self.kill_update_component_license_granted_thread()
     46 
     47     def kill_update_thread(self):
     48         if self._update_thread is not None:
     49             self._update_thread.kill()
     50             self._update_thread = None
     51 
     52     def kill_update_component_license_granted_thread(self):
     53         if self._update_component_license_granted_thread is not None:
     54             self._update_component_license_granted_thread.kill()
     55             self._update_component_license_granted_thread = None
     56 
     57     def connect_signals(self):
     58         current_skin_design_signals.on_component_instance_license_to_use_changed.connect(self.on_component_instance_license_to_use_changed)
     59         ship_skin_signals.on_component_license_granted.connect(self.on_component_license_granted)
     60 
     61     def disconnect_signals(self):
     62         current_skin_design_signals.on_component_instance_license_to_use_changed.disconnect(self.on_component_instance_license_to_use_changed)
     63         ship_skin_signals.on_component_license_granted.disconnect(self.on_component_license_granted)
     64 
     65     def on_component_instance_license_to_use_changed(self, component_id, license):
     66         if self.component_instance and self.component_instance.component_id == component_id:
     67             self.update_async()
     68 
     69     def on_component_license_granted(self, component_id, license_type, quantity):
     70         self.update_component_license_granted_async(component_id, license_type)
     71 
     72     def update_component_license_granted_async(self, component_id, license_type):
     73         self.kill_update_component_license_granted_thread()
     74         self._update_component_license_granted_thread = uthread2.start_tasklet(self.update_component_license_granted, component_id, license_type)
     75 
     76     def update_component_license_granted(self, component_id, license_type):
     77         if self.component_instance and self.component_instance.component_id == component_id:
     78             self.load_license_status()
     79             if not self.has_available_license:
     80                 return
     81             if license_type == ComponentLicenseType.UNLIMITED and self.unbound_license:
     82                 self.component_instance.component_license_to_use = self.unbound_license
     83             elif license_type == ComponentLicenseType.LIMITED and self.bound_license:
     84                 self.component_instance.component_license_to_use = self.bound_license
     85 
     86     def construct_layout(self):
     87         self.construct_unbound_indicator()
     88         self.construct_slash()
     89         self.construct_bound_indicator()
     90 
     91     def construct_unbound_indicator(self):
     92         self.unbound_indicator = UnboundLicenseIndicator(name='unbound_indicator', parent=self, align=Align.TOLEFT)
     93         self.unbound_indicator.OnClick = self.on_unbound_click
     94 
     95     def construct_slash(self):
     96         self.slash = SlashDivider(name='slash', parent=self, align=Align.TOLEFT, padTop=1)
     97 
     98     def construct_bound_indicator(self):
     99         self.bound_indicator = BoundLicenseIndicator(name='bound_indicator', parent=self, align=Align.TOLEFT)
    100         self.bound_indicator.OnClick = self.on_bound_click
    101 
    102     def update_async(self):
    103         self.kill_update_thread()
    104         self._update_thread = uthread2.start_tasklet(self.update)
    105 
    106     def update(self):
    107         self.load_license_status()
    108         self.display = self.has_available_license
    109         self.update_unbound_indicator()
    110         self.update_bound_indicator()
    111         self.update_selection()
    112         self.update_slash()
    113         if self.component_instance and self.component_instance.component_license_to_use:
    114             self.show()
    115         else:
    116             self.hide()
    117 
    118     def load_license_status(self):
    119         self.bound_license = self.unbound_license = None
    120         if not self.component_instance:
    121             return
    122         try:
    123             self.bound_license = get_ship_skin_component_svc().get_bound_license(component_id=self.component_instance.component_id, component_type=self.component_instance.get_component_data().category)
    124             self.unbound_license = get_ship_skin_component_svc().get_unbound_license(component_id=self.component_instance.component_id, component_type=self.component_instance.get_component_data().category)
    125         except (GenericException, TimeoutException):
    126             message_player(GetByLabel('UI/Common/CannotConnectToServer'))
    127 
    128     def update_unbound_indicator(self):
    129         self.unbound_indicator.component_license = self.unbound_license
    130         self.unbound_indicator.component_instance = self.component_instance
    131 
    132     def update_bound_indicator(self):
    133         self.bound_indicator.component_license = self.bound_license
    134         self.bound_indicator.component_instance = self.component_instance
    135 
    136     def update_selection(self):
    137         self.bound_indicator.is_selected = False
    138         self.unbound_indicator.is_selected = False
    139         if not self.component_instance:
    140             return
    141         license = self.component_instance.get_component_license()
    142         if not license:
    143             return
    144         if license.license_type == ComponentLicenseType.UNLIMITED:
    145             self.unbound_indicator.is_selected = True
    146         elif license.license_type == ComponentLicenseType.LIMITED:
    147             self.bound_indicator.is_selected = self.has_bound_uses_remaining
    148 
    149     def update_slash(self):
    150         self.slash.display = self.unbound_license is not None and self.bound_license is not None
    151         if self.bound_indicator.is_selected:
    152             self.slash.close()
    153         else:
    154             self.slash.open()
    155 
    156     def show(self, animate = True):
    157         self.state = uiconst.UI_PICKCHILDREN
    158         if animate:
    159             animations.FadeTo(self, self.opacity, 1.0, duration=0.2)
    160         else:
    161             self.opacity = 1.0
    162 
    163     def hide(self, animate = True):
    164         self.state = uiconst.UI_DISABLED
    165         if animate:
    166             animations.FadeTo(self, self.opacity, 0.0, duration=0.2)
    167         else:
    168             self.opacity = 0.0
    169 
    170     def on_unbound_click(self, *args):
    171         self.unbound_indicator.is_selected = True
    172         self.bound_indicator.is_selected = False
    173         self.component_instance.component_license_to_use = self.unbound_license
    174         sequence_ui_signals.on_component_license_selection_changed()
    175 
    176     def on_bound_click(self, *args):
    177         self.bound_indicator.is_selected = True
    178         self.unbound_indicator.is_selected = False
    179         self.component_instance.component_license_to_use = self.bound_license
    180         sequence_ui_signals.on_component_license_selection_changed()
    181 
    182     @property
    183     def has_available_license(self):
    184         return self.bound_license is not None or self.unbound_license is not None
    185 
    186     @property
    187     def has_bound_uses_remaining(self):
    188         if self.bound_license:
    189             return self.bound_license.remaining_license_uses and self.bound_license.remaining_license_uses > 0
    190         return False
    191 
    192     @property
    193     def component_instance(self):
    194         return self._component_instance
    195 
    196     @component_instance.setter
    197     def component_instance(self, value):
    198         if self._component_instance == value:
    199             return
    200         self._component_instance = value
    201         self.update_async()
    202 
    203 
    204 class BaseLicenseIndicator(Container):
    205     default_width = 28
    206     default_height = 20
    207     default_state = uiconst.UI_NORMAL
    208 
    209     def __init__(self, component_license = None, component_instance = None, *args, **kwargs):
    210         super(BaseLicenseIndicator, self).__init__(*args, **kwargs)
    211         self._component_license = component_license
    212         self._component_instance = component_instance
    213         self._is_selected = False
    214         self.construct_layout()
    215         self.update()
    216 
    217     def construct_layout(self):
    218         self.construct_background()
    219 
    220     def construct_background(self):
    221         self.background_sprite = StretchSpriteHorizontal(name='background_sprite', parent=self, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/Classes/Cosmetics/Ship/license_indicator_bg.png', color=eveColor.SMOKE_BLUE, opacity=0.3, leftEdgeSize=8, rightEdgeSize=8)
    222 
    223     def update(self):
    224         self.update_display()
    225         if not self.display:
    226             return
    227         self.update_width()
    228         self.update_background()
    229 
    230     def update_display(self):
    231         self.display = self.component_license is not None
    232 
    233     def update_width(self):
    234         self.width = self.default_width
    235 
    236     def update_background(self):
    237         self.background_sprite.pos = (0,
    238          0,
    239          self.width,
    240          self.default_height)
    241         target_opacity = 1.0 if self.is_selected else 0.3
    242         animations.FadeTo(self.background_sprite, self.background_sprite.opacity, target_opacity, duration=0.2)
    243 
    244     @property
    245     def is_selected(self):
    246         return self._is_selected
    247 
    248     @is_selected.setter
    249     def is_selected(self, value):
    250         if self._is_selected == value:
    251             return
    252         self._is_selected = value
    253         self.update()
    254 
    255     @property
    256     def component_license(self):
    257         return self._component_license
    258 
    259     @component_license.setter
    260     def component_license(self, value):
    261         if self._component_license == value:
    262             return
    263         self._component_license = value
    264         self.update()
    265 
    266     @property
    267     def component_instance(self):
    268         return self._component_instance
    269 
    270     @component_instance.setter
    271     def component_instance(self, value):
    272         if self._component_instance == value:
    273             return
    274         self._component_instance = value
    275         self.update()
    276 
    277 
    278 class BoundLicenseIndicator(BaseLicenseIndicator):
    279 
    280     def construct_layout(self):
    281         self.label = TextBody(name='label', parent=self, align=Align.CENTER, opacity=0.5)
    282         super(BoundLicenseIndicator, self).construct_layout()
    283 
    284     def update(self):
    285         super(BoundLicenseIndicator, self).update()
    286         self.update_label()
    287 
    288     def update_display(self):
    289         self.display = self.has_uses_remaining
    290 
    291     def update_width(self):
    292         text_width, _ = TextBody.MeasureTextSize(self.label_text)
    293         self.width = max(self.default_width, text_width + 16)
    294 
    295     def update_label(self):
    296         self.label.text = self.label_text
    297         target_opacity = 1.0 if self.is_selected else 0.5
    298         animations.FadeTo(self.label, self.label.opacity, target_opacity, duration=0.2)
    299 
    300     @property
    301     def has_uses_remaining(self):
    302         if self.component_license:
    303             return self.component_license.remaining_license_uses and self.component_license.remaining_license_uses > 0
    304         return False
    305 
    306     @property
    307     def label_text(self):
    308         if self.component_license:
    309             return self.component_license.remaining_license_uses
    310         return ''
    311 
    312     def GetHint(self):
    313         return GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/UseLimitedDesignElement') + '\n' + GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/NumLimitedDesignElementsAvailable', num_available=self.component_license.remaining_license_uses)
    314 
    315 
    316 class UnboundLicenseIndicator(BaseLicenseIndicator):
    317 
    318     def construct_layout(self):
    319         self.icon = Sprite(name='icon', parent=self, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath=eveicon.infinity, color=TextColor.NORMAL, opacity=0.5, pos=(0, 0, 16, 16))
    320         super(UnboundLicenseIndicator, self).construct_layout()
    321 
    322     def update(self):
    323         super(UnboundLicenseIndicator, self).update()
    324         self.update_icon()
    325 
    326     def update_icon(self):
    327         target_opacity = 1.0 if self.is_selected else 0.5
    328         animations.FadeTo(self.icon, self.icon.opacity, target_opacity, duration=0.2)
    329 
    330     def GetHint(self):
    331         return GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/UnlimitedDesignElementRequirement', type_id=self.component_instance.sequence_binder_type_id, amount=self.component_instance.sequence_binder_amount_required_if_bound)
    332 
    333 
    334 class SlashDivider(Container):
    335     default_width = 13
    336     default_height = 18
    337 
    338     def __init__(self, *args, **kwargs):
    339         super(SlashDivider, self).__init__(*args, **kwargs)
    340         self.construct_layout()
    341 
    342     def construct_layout(self):
    343         self.open_sprite = Sprite(name='open_sprite', parent=self, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/Classes/Cosmetics/Ship/slash_open.png', color=eveColor.FOCUS_BLUE, pos=(0, 0, 33, 38), display=False)
    344         self.closed_sprite = Sprite(name='closed_sprite', parent=self, align=Align.CENTER, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/Classes/Cosmetics/Ship/slash_closed.png', color=eveColor.FOCUS_BLUE, pos=(0, 0, 27, 26))
    345 
    346     def open(self):
    347         self.open_sprite.display = True
    348         self.closed_sprite.display = False
    349 
    350     def close(self):
    351         self.open_sprite.display = False
    352         self.closed_sprite.display = True