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

componentPanel.py (15740B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\cosmetics\ship\pages\studio\componentPanel.py
      2 import eveicon
      3 import uthread2
      4 from carbon.common.script.sys.serviceConst import ROLE_QA
      5 from carbonui import Align, TextColor, uiconst
      6 from carbonui.text.styles import TextBody
      7 from carbonui.button.group import ButtonGroup, ButtonSizeMode
      8 from carbonui.control.button import Button
      9 from carbonui.control.buttonIcon import ButtonIcon
     10 from carbonui.control.checkbox import Checkbox
     11 from carbonui.control.scrollContainer import ScrollContainer
     12 from carbonui.control.tabGroup import TabGroup
     13 from carbonui.primitives.container import Container
     14 from carbonui.primitives.flowcontainer import FlowContainer
     15 from carbonui.primitives.frame import Frame
     16 from carbonui.primitives.sprite import Sprite
     17 from carbonui.uianimations import animations
     18 from carbonui.uiconst import VK_DOWN, VK_UP, VK_LEFT, VK_RIGHT, VK_PRIOR, VK_NEXT
     19 from carbonui.uicore import uicore
     20 from collections import defaultdict
     21 from cosmetics.client.shipSkinComponentSvc import get_ship_skin_component_svc
     22 from cosmetics.client.ships import ship_skin_signals
     23 from cosmetics.client.ships.qa.settings import show_unpublished_components
     24 from cosmetics.client.ships.skins.live_data import current_skin_design
     25 from cosmetics.common.ships.skins.live_data.component_license_type import ComponentLicenseType
     26 from cosmetics.common.ships.skins.static_data.component_finish import ComponentFinish
     27 from cosmetics.common.ships.skins.static_data.slot import SlotsDataLoader
     28 from eve.client.script.ui.control.toggleButtonGroup import ToggleButtonGroup
     29 from eve.client.script.ui.cosmetics.ship.const import COMPONENT_CATEGORY_NAMES, SkinrPage, SLOT_ICONS
     30 from eve.client.script.ui.cosmetics.ship.pages import current_page
     31 from eve.client.script.ui.cosmetics.ship.pages.studio import studioSignals
     32 from eve.client.script.ui.cosmetics.ship.pages.studio.componentLicenseEntry import ComponentEntry
     33 from eve.client.script.ui.cosmetics.ship.pages.studio.studioSettings import only_shown_owned_design_elements_setting
     34 from localization import GetByLabel
     35 from signals import Signal
     36 FINISH_ALL = 0
     37 LABEL_BY_COMPONENT_FINISH = {ComponentFinish.MATTE: 'UI/Personalization/ShipSkins/SKINR/FinishMatte',
     38  ComponentFinish.SATIN: 'UI/Personalization/ShipSkins/SKINR/FinishSatin',
     39  ComponentFinish.GLOSS: 'UI/Personalization/ShipSkins/SKINR/FinishGloss'}
     40 
     41 class ComponentPanel(Container):
     42     default_state = uiconst.UI_DISABLED
     43     default_width = 360
     44     default_opacity = 0.0
     45     RADIUS_OFFSET = 50
     46     isTabStop = True
     47 
     48     def __init__(self, slot_data, *args, **kwargs):
     49         super(ComponentPanel, self).__init__(*args, **kwargs)
     50         self.slot_data = slot_data
     51         self.is_active = False
     52         self.on_close_btn = Signal('on_close_btn')
     53         self._reconstruct_thread = None
     54         self._selected_category = None
     55         self.construct_layout()
     56         self.connect_signals()
     57 
     58     def Close(self):
     59         try:
     60             self.kill_reconstruct_thread()
     61             self.disconnect_signals()
     62         finally:
     63             super(ComponentPanel, self).Close()
     64 
     65     def kill_reconstruct_thread(self):
     66         if self._reconstruct_thread is not None:
     67             self._reconstruct_thread.kill()
     68             self._reconstruct_thread = None
     69 
     70     def connect_signals(self):
     71         studioSignals.on_scene_zoom.connect(self.on_scene_zoom)
     72         ship_skin_signals.on_component_license_granted.connect(self._on_component_license_granted)
     73         ship_skin_signals.on_component_license_cache_invalidated.connect(self._on_component_license_cache_invalidated)
     74         ship_skin_signals.on_skin_sequencing_job_updated.connect(self.on_skin_sequencing_job_updated)
     75         only_shown_owned_design_elements_setting.on_change.connect(self.on_only_show_owned_design_elements_setting)
     76         show_unpublished_components.on_change.connect(self._on_qa_show_unpublished)
     77 
     78     def disconnect_signals(self):
     79         studioSignals.on_scene_zoom.disconnect(self.on_scene_zoom)
     80         ship_skin_signals.on_component_license_granted.disconnect(self._on_component_license_granted)
     81         ship_skin_signals.on_component_license_cache_invalidated.disconnect(self._on_component_license_cache_invalidated)
     82         ship_skin_signals.on_skin_sequencing_job_updated.disconnect(self.on_skin_sequencing_job_updated)
     83         only_shown_owned_design_elements_setting.on_change.disconnect(self.on_only_show_owned_design_elements_setting)
     84         show_unpublished_components.on_change.disconnect(self._on_qa_show_unpublished)
     85 
     86     def on_only_show_owned_design_elements_setting(self, *args):
     87         self.reconstruct_entries()
     88 
     89     def construct_layout(self):
     90         self.content = Container(name='content', parent=self, padding=16)
     91         self.construct_header()
     92         self.category_toggle_button_group = ToggleButtonGroup(name='category_toggle_button_group', parent=self.content, align=Align.TOTOP, padTop=4, callback=self.on_category_toggle_buttons)
     93         self.finish_tab_group = TabGroup(name='finish_tab_group', parent=self.content, callback=self.on_finish_tab_group, show_line=False, padTop=4, padBottom=-4)
     94         Checkbox(name='only_ownned_checkbox', parent=self.content, align=Align.TOTOP, text=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/OnlyShowOwned'), setting=only_shown_owned_design_elements_setting, padTop=16)
     95         self.button_group = ButtonGroup(parent=self.content, align=Align.TOBOTTOM, button_size_mode=ButtonSizeMode.STRETCH, padTop=4)
     96         Button(name='buy_more_btn', parent=self.button_group, label=GetByLabel('UI/Personalization/ShipSkins/SKINR/Studio/BuyMore'), func=self.on_buy_more_btn)
     97         self.scroll_container = ScrollContainer(name='scroll_container', parent=self.content, align=Align.TOALL, padTop=8)
     98         self.scroll_container.OnKeyDown = self.OnKeyDown
     99         self.flow_container = FlowContainer(name='flow_container', parent=self.scroll_container, align=Align.TOTOP, contentSpacing=(4, 4))
    100 
    101     def construct_header(self):
    102         header_cont = Container(name='header_cont', parent=self.content, align=Align.TOTOP, height=36)
    103         icon_cont = Container(name='icon_cont', parent=header_cont, align=Align.TOLEFT, width=36)
    104         Frame(bgParent=icon_cont, color=(1, 1, 1, 0.05))
    105         Sprite(parent=icon_cont, texturePath=SLOT_ICONS[self.slot_data.unique_id], pos=(0, 0, 32, 32), align=Align.CENTER, color=TextColor.HIGHLIGHT)
    106         text_cont = Container(parent=header_cont, bgColor=(1, 1, 1, 0.05), clipChildren=True, padLeft=4)
    107         TextBody(parent=text_cont, align=Align.VERTICALLY_CENTERED, text=self.slot_data.name, color=TextColor.HIGHLIGHT, autoFadeSides=10, padLeft=16, padRight=32)
    108         ButtonIcon(parent=header_cont, align=Align.CENTERRIGHT, texturePath=eveicon.close, func=self.on_close_btn)
    109 
    110     def on_finish_tab_group(self, *args):
    111         self.reconstruct_entries()
    112 
    113     def on_buy_more_btn(self, *args):
    114         current_page.set_page_id(page_id=SkinrPage.STORE_COMPONENTS, page_args=self.get_selected_category())
    115 
    116     def _on_qa_show_unpublished(self, *args):
    117         if session.role & ROLE_QA:
    118             self.reconstruct_entries()
    119 
    120     def get_selected_category(self):
    121         return self.category_toggle_button_group.GetSelected()
    122 
    123     def reconstruct_category_buttons(self):
    124         self.kill_reconstruct_thread()
    125         self._reconstruct_thread = uthread2.start_tasklet(self.reconstruct_category_buttons_async)
    126 
    127     def reconstruct_category_buttons_async(self):
    128         try:
    129             self.category_toggle_button_group.Flush()
    130             licenses_by_category = self.get_components_by_category()
    131             categories = licenses_by_category.keys()
    132             for category in categories:
    133                 category_name = GetByLabel(COMPONENT_CATEGORY_NAMES.get(category))
    134                 self.category_toggle_button_group.AddButton(label=category_name, btnID=category)
    135 
    136             self.category_toggle_button_group.display = len(categories) > 1
    137             category_id = self.get_selected_category_id(licenses_by_category)
    138             if category_id:
    139                 self.category_toggle_button_group.SelectByID(category_id)
    140         finally:
    141             self._reconstruct_thread = None
    142 
    143     def get_components_by_category(self):
    144         slot_id = self.slot_data.unique_id
    145         components_by_category = defaultdict(list)
    146         components = SlotsDataLoader.get_allowed_component(slot_id).values()
    147         for component_data in components:
    148             category = component_data.category
    149             components_by_category[category].append(component_data)
    150 
    151         return components_by_category
    152 
    153     def check_tab_exists(self, finish):
    154         if finish == FINISH_ALL:
    155             finish_label = GetByLabel('UI/Common/All')
    156         else:
    157             finish_label = GetByLabel(LABEL_BY_COMPONENT_FINISH[finish])
    158         label, name = self.finish_tab_group.GetNameLabel(finish_label)
    159         uiID = self.finish_tab_group.GetUIID(name, finish)
    160         exist = list(filter(lambda x: x.parent == self.finish_tab_group.tabsCont and str(x.tabID) == str(uiID), self.finish_tab_group.mytabs))
    161         return len(exist) > 0
    162 
    163     def reconstruct_finish_tabs(self):
    164         selected_finish = self.finish_tab_group.GetSelectedID()
    165         self.finish_tab_group.Flush()
    166         components = self.get_components_to_show()
    167         finishes = {c.finish for c in components}
    168         if not self.check_tab_exists(FINISH_ALL):
    169             self.finish_tab_group.AddTab(label=GetByLabel('UI/Common/All'), tabID=FINISH_ALL)
    170         if len(finishes) > 1:
    171             self.finish_tab_group.display = True
    172             for finish in (ComponentFinish.MATTE, ComponentFinish.SATIN, ComponentFinish.GLOSS):
    173                 if finish not in finishes:
    174                     continue
    175                 if not self.check_tab_exists(finish):
    176                     self.finish_tab_group.AddTab(label=GetByLabel(LABEL_BY_COMPONENT_FINISH[finish]), tabID=finish)
    177 
    178         else:
    179             self.finish_tab_group.display = False
    180         if selected_finish is None:
    181             selected_finish = FINISH_ALL
    182         self.finish_tab_group.SelectByID(selected_finish, useCallback=False)
    183 
    184     def get_selected_category_id(self, licenses_by_category):
    185         if self._selected_category:
    186             return self._selected_category
    187         component_instance = current_skin_design.get().slot_layout.slots[self.slot_data.unique_id]
    188         if component_instance:
    189             return component_instance.category
    190         elif len(licenses_by_category.keys()) > 0:
    191             return licenses_by_category.keys()[0]
    192         else:
    193             return None
    194 
    195     def on_category_toggle_buttons(self, new_category, old_category):
    196         self._selected_category = new_category
    197         self.reconstruct_finish_tabs()
    198         self.reconstruct_entries()
    199 
    200     def reconstruct_entries(self):
    201         self.flow_container.Flush()
    202         components = self.get_components_to_show()
    203         fitted_component = current_skin_design.get().slot_layout.slots.get(self.slot_data.unique_id)
    204         fitted_component_id = fitted_component.component_id if fitted_component else None
    205         for component_data in components:
    206             owned_licenses_by_type = get_ship_skin_component_svc().get_owned_licenses_by_type(component_data.component_id)
    207             is_selected = component_data.component_id == fitted_component_id
    208             ComponentEntry(parent=self.flow_container, slot_id=self.slot_data.unique_id, component_data=component_data, is_selected=is_selected, limited_license=owned_licenses_by_type.get(ComponentLicenseType.LIMITED, None), unlimited_license=owned_licenses_by_type.get(ComponentLicenseType.UNLIMITED, None))
    209 
    210     def get_components_to_show(self):
    211         components = SlotsDataLoader.get_allowed_component(self.slot_data.unique_id).values()
    212         if only_shown_owned_design_elements_setting.is_enabled():
    213             owned_licenses = get_ship_skin_component_svc().get_all_owned_licenses()
    214             owned_ids = {l.component_id for l in owned_licenses}
    215             components = [ c for c in components if c.component_id in owned_ids ]
    216         show_unpublished = show_unpublished_components.is_enabled()
    217         if not session.role & ROLE_QA or not show_unpublished:
    218             components = [ c for c in components if c.published ]
    219         category_id = self.get_selected_category()
    220         if category_id:
    221             components = [ c for c in components if c.category == category_id ]
    222         selected_finish = self.finish_tab_group.GetSelectedID()
    223         if selected_finish:
    224             components = [ c for c in components if c.finish == selected_finish ]
    225         components = sorted(components, key=lambda component: component.color_shade_sort_index, reverse=False)
    226         return components
    227 
    228     def get_components_in_selected_category(self):
    229         components_by_category = self.get_components_by_category()
    230         components = components_by_category[self.get_selected_category()]
    231         return components
    232 
    233     def on_slot_selected(self, slot_id):
    234         if slot_id == self.slot_data.unique_id:
    235             self.reconstruct_tabs()
    236             self.appear()
    237             uicore.registry.SetFocus(self)
    238         else:
    239             self.disappear()
    240 
    241     def reconstruct_tabs(self):
    242         self.reconstruct_category_buttons()
    243         self.reconstruct_finish_tabs()
    244 
    245     def disappear(self):
    246         self.is_active = False
    247         self.state = uiconst.UI_DISABLED
    248         self.StopAnimations()
    249         self.opacity = 0.0
    250 
    251     def appear(self):
    252         if self.is_active:
    253             return
    254         self.is_active = True
    255         self.state = uiconst.UI_PICKCHILDREN
    256         animations.FadeTo(self, self.opacity, 1.0, duration=4 * uiconst.TIME_ENTRY)
    257 
    258     def on_scene_zoom(self, is_zoomed):
    259         if not self.is_active:
    260             return
    261         target_opacity = 0.0 if is_zoomed else 1.0
    262         time_offset = 0.0 if is_zoomed else 0.15
    263         animations.FadeTo(self, self.opacity, target_opacity, duration=0.15, timeOffset=time_offset)
    264 
    265     def OnKeyDown(self, key, *args):
    266         diff = None
    267         if key == VK_LEFT:
    268             diff = -1
    269         elif key == VK_RIGHT:
    270             diff = 1
    271         elif key == VK_UP:
    272             diff = -3
    273         elif key == VK_DOWN:
    274             diff = 3
    275         elif key == VK_PRIOR:
    276             self.scroll_container.ScrollByPage(up=True)
    277         elif key == VK_NEXT:
    278             self.scroll_container.ScrollByPage(up=False)
    279         if diff:
    280             self._select_new_component(diff)
    281 
    282     def _select_new_component(self, diff):
    283         selected = self.get_selected()
    284         components = self.flow_container.children
    285         if selected:
    286             idx = components.index(selected) + diff
    287         else:
    288             idx = 0
    289         if 0 <= idx < len(components):
    290             components[idx].toggle_selected()
    291 
    292     def get_selected(self):
    293         for component in self.flow_container.children:
    294             if component.is_selected:
    295                 return component
    296 
    297     def select_component(self, component_instance):
    298         for component in self.flow_container.children:
    299             if component_instance is None:
    300                 component.is_selected = False
    301             else:
    302                 component.is_selected = component.component_data.component_id == component_instance.component_id
    303 
    304     def _on_component_license_granted(self, *args):
    305         self._on_licenses_updated()
    306 
    307     def _on_component_license_cache_invalidated(self, _licenses_flushed):
    308         self._on_licenses_updated()
    309 
    310     def on_skin_sequencing_job_updated(self, *args):
    311         self._on_licenses_updated()
    312 
    313     @uthread2.debounce(1.0)
    314     def _on_licenses_updated(self):
    315         self.reconstruct_tabs()