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

shipInfoWindow.py (38418B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\info\shipInfoWindow.py
      2 import eveicon
      3 import evetypes
      4 import localization
      5 import uthread
      6 import uthread2
      7 import trinity
      8 from eve.client.script.ui.shared.skins.controller import ShipInfoSkinPanelController
      9 from eveservices.menu import GetMenuService
     10 import eveui
     11 from eveui import CharacterPortrait
     12 from carbon.common.script.sys.serviceConst import ROLE_QA
     13 from carbonui import uiconst, Align, PickState, TextColor
     14 from carbonui.text.styles import TextBody, TextDetail
     15 from carbonui.control.dragdrop.dragdata import ShipDragData
     16 from carbonui.control.sideNavigation import SideNavigationSplitView, SideNavigation, SideNavigationEntry, SideNavigationEntryInterface
     17 from carbonui.primitives.container import Container
     18 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     19 from carbonui.primitives.fill import Fill
     20 from carbonui.primitives.frame import Frame
     21 from carbonui.primitives.sprite import Sprite
     22 from clientsettings.setting import SessionSettingBool, SessionSettingEnum
     23 from carbonui.uianimations import animations
     24 from carbonui.window.control.action import WindowMenuAction, WindowActionImportance
     25 from eve.client.script.ui import eveColor, eveThemeColor
     26 from eve.client.script.ui.control.scenecontainer import SceneContainerBaseNavigation
     27 from eve.client.script.ui.services.menuSvcExtras.openFunctions import SimulateFitting
     28 from eve.client.script.ui.shared.fittingScreen.tryFit import GetParentShipInfo
     29 from eve.client.script.ui.shared.info.baseInfoWindow import BaseInfoWindow
     30 from eve.client.script.ui.shared.info.shipInfoConst import GetAnimDuration, TOP_DOWN_NOSE_UP_ALT, get_sound_by_type_id, GetMassBasedSoundOffset, INFO_TABS, FULLY_MINIMIZED, MINIMIZED, EXPANDED, CONTENT_PADDING
     31 from eve.client.script.ui.shared.info.shipInfoPanels.panelConst import SHIP_INFO_PANELS, TAB_OVERVIEW, TAB_FITTING, TAB_SKINS
     32 from eve.client.script.ui.shared.info.shipInfoPreviewScene import ShipInfoPreviewSceneContainer
     33 from inventorycommon.typeHelpers import GetHoloIconPath, GetIconFile
     34 from inventorycommon.util import GetSubSystemTypeIDs, IsModularShip, IsSubsystemFlag
     35 from inventorycommon.const import flagCargo, flagShipHangar, groupCapsule
     36 from inventoryrestrictions import can_view_market_details
     37 from carbonui.uicore import uicore
     38 from eve.common.script.sys.eveCfg import InShipInSpace
     39 from eve.common.script.sys import idCheckers
     40 from utillib import KeyVal
     41 import shipgroup
     42 is_expanded_setting = SessionSettingBool(False)
     43 EXPANDED_WIDTH = 640
     44 MINIMIZED_ZOOM = 0.1
     45 EXPANDED_ZOOM = 0.3
     46 MINIMIZED_ZOOM_RANGE = [1.6, 3.5]
     47 EXPANDED_ZOOM_RANGE = [1.5, 3.5]
     48 MINIMIZED_BG_COLOR = (0,
     49  0,
     50  0,
     51  1)
     52 MINIMIZED_BG_OPACITY = 0.9
     53 MINIMIZED_POSITION_OVERRIDE = TOP_DOWN_NOSE_UP_ALT
     54 LAST_TAB_SETTING = SessionSettingEnum(TAB_OVERVIEW, INFO_TABS)
     55 
     56 class ShipInfoWindowController(object):
     57 
     58     def __init__(self, select_tab, get_ship_length, reset_skin):
     59         self._select_tab = select_tab
     60         self._get_ship_length = get_ship_length
     61         self._skin_controller = None
     62         self._type_id = None
     63         self._item_id = None
     64         self._item = None
     65         self._parent_id = None
     66         self._abstract_info = None
     67         self._owner_id = None
     68         self._owner_data = None
     69         self._ship_contents = None
     70         self._fetching_owner_data = False
     71         self._reset_skin = reset_skin
     72 
     73     def set_values(self, type_id, item_id, item, parent_id, abstract_info):
     74         self._type_id = type_id
     75         self._item_id = item_id
     76         self._item = item
     77         self._parent_id = parent_id
     78         self._abstract_info = abstract_info
     79         self._owner_id = self._get_owner_id()
     80         self._owner_data = None
     81         self._ship_contents = None
     82         if self._owner_id and not self.is_owned_by_me:
     83             self._fetching_owner_data = True
     84             uthread2.start_tasklet(self._update_owner)
     85 
     86     def set_skin_controller(self, skin_controller):
     87         self._skin_controller = skin_controller
     88 
     89     def select_tab(self, tab_id):
     90         self._select_tab(tab_id)
     91 
     92     def reset_skin(self):
     93         self._reset_skin()
     94 
     95     @property
     96     def skin_controller(self):
     97         return self._skin_controller
     98 
     99     @property
    100     def item_id(self):
    101         return self._item_id
    102 
    103     @property
    104     def item(self):
    105         return self._item
    106 
    107     @item.setter
    108     def item(self, value):
    109         if value:
    110             self._item = value
    111 
    112     @property
    113     def type_id(self):
    114         return self._type_id
    115 
    116     @property
    117     def group_id(self):
    118         return evetypes.GetGroupID(self.type_id)
    119 
    120     @property
    121     def owner_id(self):
    122         return self._owner_id
    123 
    124     @property
    125     def owner_corporation_id(self):
    126         if not self.owner_id:
    127             return None
    128         if self.is_owned_by_me:
    129             return session.corpid
    130         owner_data = self.get_owner_data()
    131         if owner_data:
    132             return owner_data.corpID
    133 
    134     @property
    135     def long_axis_length(self):
    136         return self._get_ship_length()
    137 
    138     @property
    139     def is_assembled(self):
    140         return self.item_id == session.shipid or self._item and self._item.singleton
    141 
    142     @property
    143     def is_owned_by_me(self):
    144         return self.owner_id == session.charid
    145 
    146     @property
    147     def is_simulated(self):
    148         return self.item_id is not None and isinstance(self.item_id, basestring)
    149 
    150     def get_custom_name(self):
    151         if self.owner_id and self.item_id in cfg.evelocations.data:
    152             return cfg.evelocations.Get(self.item_id).name
    153 
    154     def get_ship_name(self):
    155         return self.get_custom_name() or evetypes.GetName(self.type_id)
    156 
    157     def get_type_name(self):
    158         return evetypes.GetName(self.type_id)
    159 
    160     def get_class_name(self):
    161         return shipgroup.get_ship_group_name(self.type_id)
    162 
    163     def get_description(self):
    164         return evetypes.GetDescription(self.type_id)
    165 
    166     def get_owner_name(self):
    167         owner_id = self.owner_id
    168         if owner_id:
    169             return cfg.eveowners.Get(owner_id).name
    170         return ''
    171 
    172     def get_owner_data(self):
    173         while self._fetching_owner_data:
    174             uthread2.Yield()
    175 
    176         return self._owner_data
    177 
    178     def _get_owner_id(self):
    179         if not self.item_id:
    180             return
    181         if self.item_id == session.shipid:
    182             return session.charid
    183         if InShipInSpace():
    184             owner_id = sm.GetService('michelle').GetCharIDFromShipID(self.item_id)
    185             if owner_id:
    186                 return owner_id
    187         owner_id = None
    188         if self._item and self._item.ownerID and self._item.singleton:
    189             owner_id = self._item.ownerID
    190         elif self._abstract_info:
    191             try:
    192                 owner_id = self._abstract_info.ownerID
    193             except:
    194                 pass
    195 
    196         if owner_id and idCheckers.IsEvePlayerCharacter(owner_id):
    197             return owner_id
    198 
    199     def _update_owner(self):
    200         self._owner_data = sm.GetService('crimewatchSvc').GetSlimItemDataForCharID(self.owner_id)
    201         self._fetching_owner_data = False
    202 
    203     def get_subsystems(self):
    204         if not IsModularShip(self.type_id) or not self.item_id:
    205             return None
    206         if InShipInSpace() and self.item_id != session.shipid:
    207             slimItem = sm.GetService('michelle').GetItem(self.item_id)
    208             if slimItem and slimItem.modules:
    209                 return [ type_id for item_id, type_id, flag_id in slimItem.modules if IsSubsystemFlag(flag_id) ]
    210         return GetSubSystemTypeIDs(self.item_id, self.type_id)
    211 
    212     def get_ship_contents(self):
    213         if self._ship_contents is None or self.item_id == session.shipid:
    214             self._ship_contents = self._get_ship_contents()
    215         return self._ship_contents
    216 
    217     def _get_ship_contents(self):
    218         if self.item_id is None:
    219             return []
    220         if session.shipid == self.item_id:
    221             shipinfo = sm.GetService('godma').GetItem(self.item_id)
    222             if shipinfo is not None and getattr(shipinfo, 'inventory', None) is not None:
    223                 return shipinfo.inventory.List()
    224         if sm.GetService('info').IsItemSimulated(self.item_id):
    225             return self._get_simulated_ship_contents()
    226         if self.item is None:
    227             return []
    228         if not self.item.singleton:
    229             return []
    230         if self.item.ownerID == session.charid and self.item.locationID:
    231             dockable_location_id = self.item.locationID
    232             solar_system_id = None
    233             if self.item.flagID in (flagCargo, flagShipHangar):
    234                 parent_ship_info = GetParentShipInfo(self.item)
    235                 if parent_ship_info:
    236                     dockable_location_id, solar_system_id = parent_ship_info
    237             inventoryMgr = sm.GetService('invCache').GetInventoryMgr()
    238             try:
    239                 return inventoryMgr.GetContainerContents(self.item.itemID, dockable_location_id, solar_system_id)
    240             except AttributeError:
    241                 pass
    242             except RuntimeError as e:
    243                 if e[0] == 'InvalidLocationID':
    244                     pass
    245                 else:
    246                     raise e
    247 
    248         return []
    249 
    250     def _get_simulated_ship_contents(self):
    251         dogma_location = sm.GetService('clientDogmaIM').GetFittingDogmaLocation()
    252         fitted_items = dogma_location.GetFittedItemsToShip()
    253         return fitted_items.values()
    254 
    255     def get_fitting_data(self, put_ammo_in_cargo = True):
    256         fitting_svc = sm.GetService('fittingSvc')
    257         inventory = self.get_ship_contents()
    258         fitting = KeyVal()
    259         fitting.shipTypeID = self.type_id
    260         fitting.fitData = fitting_svc.CreateFittingData(inventory, putModuleAmmoInCargo=put_ammo_in_cargo)
    261         fitting.fittingID = None
    262         fitting.description = ''
    263         shipName = self.get_ship_name()
    264         fitting.name = shipName
    265         fitting.ownerID = 0
    266         return fitting
    267 
    268     def open_simulation(self):
    269         fitting_svc = sm.GetService('fittingSvc')
    270         fitting = self.get_fitting_data(False)
    271         inventory = self.get_ship_contents()
    272         charges_to_load = fitting_svc.GetLoadedChargeTypes(inventory)
    273         SimulateFitting(fitting, charges_to_load)
    274 
    275 
    276 class ShipInfoWindow(BaseInfoWindow):
    277     __guid__ = 'form.shipinfowindow'
    278     __notifyevents__ = []
    279     SIDE_PANEL_WIDTH = 58
    280     default_width = 960
    281     default_height = 656
    282     default_minSize = (325, 450)
    283     default_name = 'shipInfoWindow'
    284     default_windowID = 'shipInfoWindow'
    285     default_iconNum = eveicon.spaceship_command
    286     default_scope = uiconst.SCOPE_INGAME
    287     default_apply_content_padding = False
    288     default_extend_content_into_header = True
    289     default_isCompactable = True
    290 
    291     def ApplyAttributes(self, attributes):
    292         self._selectedTab = TAB_OVERVIEW
    293         self._initialized = False
    294         self._current_panel = None
    295         self._load_tasklet = None
    296         self._live_preview = None
    297         self._dynamic_state = 0
    298         self._skin_controller = None
    299         self._controller = ShipInfoWindowController(select_tab=self.open_tab_by_id, get_ship_length=self._get_ship_length, reset_skin=self._reset_skin)
    300         super(ShipInfoWindow, self).ApplyAttributes(attributes)
    301         self._dynamic_state = self._get_dynamic_state()
    302         self.on_stacked_changed.connect(self._on_stacked_changed_callback)
    303         self.on_header_height_changed.connect(self._on_header_height_changed)
    304         self._last_tab_setting = LAST_TAB_SETTING
    305 
    306     def Close(self, *args, **kwargs):
    307         super(ShipInfoWindow, self).Close(*args, **kwargs)
    308         if self._skin_controller:
    309             self._skin_controller.Close()
    310 
    311     def _on_header_height_changed(self, *args, **kwargs):
    312         self.split_view.padTop = self.sr.headerParent.height
    313 
    314     def OnCompactModeEnabled(self):
    315         self.OnResizeUpdate()
    316         self._close_live_preview()
    317         self._update_preview_display_state()
    318         self.split_view.set_force_compact(True)
    319         self.side_navigation.set_force_compact(True)
    320 
    321     def OnCompactModeDisabled(self):
    322         self.OnResizeUpdate()
    323         self._construct_live_preview()
    324         self._load_ship()
    325         self._update_preview_display_state()
    326         self.split_view.set_force_compact(False)
    327         self.side_navigation.set_force_compact(False)
    328 
    329     def ConstructLayout(self):
    330         self.content.clipChildren = True
    331         self.split_view = SideNavigationSplitView(parent=self.content, align=uiconst.TOALL, is_always_expanded_setting=is_expanded_setting, expanded_panel_width=200, padTop=self.sr.headerParent.height, clipChildren=False, force_compact=self.compact)
    332         self.side_navigation = ShipInfoSideNavigation(controller=self._controller, parent=self.split_view.panel, is_expanded_func=self.split_view.is_expanded, expand_func=self.split_view.expand_panel, force_compact=self.compact)
    333         self.split_view.on_expanded_changed.connect(self.side_navigation.on_expanded_changed)
    334         self.mainContent = Container(parent=self.split_view.content, align=Align.TOALL, padding=16)
    335         self.previewCont = Container(parent=self.split_view.content, align=Align.TOALL)
    336         self.bgFade = Fill(parent=self.content, align=Align.TOALL, state=uiconst.UI_DISABLED, color=MINIMIZED_BG_COLOR, opacity=0, pickState=PickState.OFF, display=not self.compact)
    337         self._black_bg = Fill(parent=self.content, align=Align.TOALL, state=uiconst.UI_DISABLED, color=(0, 0, 0, 1), opacity=0.0)
    338         self._construct_live_preview()
    339 
    340     def _construct_live_preview(self, forced = False):
    341         if self._live_preview or self.compact and not forced:
    342             return
    343         sceneNavigation = SceneContainerBaseNavigation(name='preview_navigation', parent=self.previewCont, align=Align.TOALL, state=uiconst.UI_NORMAL, pickRadius=0)
    344         self._live_preview = ShipInfoPreviewSceneContainer(parent=self.content, align=Align.TOALL, state=uiconst.UI_DISABLED, opacity=0.0)
    345         self._live_preview.on_load_complete.connect(self._on_model_load_complete)
    346         sceneNavigation.Startup(self._live_preview)
    347         self._update_preview_display_state()
    348 
    349     def _close_live_preview(self):
    350         if not self._live_preview or self._selectedTab == TAB_SKINS:
    351             return
    352         self._live_preview.on_load_complete.disconnect(self._on_model_load_complete)
    353         self._live_preview.Close()
    354         self._live_preview = None
    355         self.previewCont.Flush()
    356 
    357     def _ReconstructInfoWindow(self, typeID, itemID = None, rec = None, parentID = None, abstractinfo = None, branchHistory = True, selectTabType = None, params = None):
    358         self._initialized = False
    359         self._controller.set_values(type_id=typeID, item_id=itemID, item=rec, parent_id=parentID, abstract_info=abstractinfo)
    360         if self._skin_controller:
    361             self._skin_controller.onChange.disconnect(self._change_skin)
    362             self._skin_controller.Close()
    363         self._skin_controller = ShipInfoSkinPanelController(itemID=itemID, ownerID=self._controller.owner_id, hullTypeID=typeID)
    364         self._skin_controller.onChange.connect(self._change_skin)
    365         self._controller.set_skin_controller(self._skin_controller)
    366         self.typeID = typeID
    367         self.itemID = itemID
    368         self.rec = rec
    369         self.parentID = parentID
    370         self.abstractinfo = abstractinfo
    371         self.params = params
    372         skin_state = None
    373         if params:
    374             skin = params.get('skin', None)
    375             if skin:
    376                 skin_state = skin
    377         self._load_ship(skin_state)
    378         self.groupID = evetypes.GetGroupID(typeID)
    379         self.categoryID = evetypes.GetCategoryID(typeID)
    380         self.update_window_controls()
    381         self._update_caption()
    382         visible_tabs = self.get_visible_tabs(typeID, itemID, rec)
    383         nav_tabs = [ (tabID, self.get_tab_class_by_type(tabID)) for tabID in visible_tabs ]
    384         self.side_navigation.reconstruct(nav_tabs, self._selectedTab)
    385         if selectTabType:
    386             selectedTabID = selectTabType
    387         elif self._current_panel and self._current_panel.is_visible(self.typeID):
    388             selectedTabID = self._current_panel.get_tab_type()
    389         else:
    390             selectedTabID = self._last_tab_setting.get()
    391         if selectedTabID not in visible_tabs:
    392             selectedTabID = TAB_OVERVIEW
    393         self.open_tab_by_id(selectedTabID, params, initialize=True)
    394         uthread2.Yield()
    395         self._set_dynamic_state(self._get_dynamic_state(), initial=True)
    396         self._initialized = True
    397         self._do_fade_in()
    398         super(ShipInfoWindow, self)._ReconstructInfoWindow(typeID, itemID, rec, parentID, abstractinfo, branchHistory, selectTabType, params)
    399 
    400     def _load_ship(self, skin_state = None):
    401         if not self._live_preview:
    402             return
    403         animations.StopAllAnimations(self._black_bg)
    404         self._black_bg.opacity = 1
    405         if skin_state is None:
    406             skin_state = self._get_skin_state()
    407         self._live_preview.preview_ship(self._controller.type_id, subSystems=self._controller.get_subsystems(), skin_state=skin_state, snap=True, skip_animation=False)
    408 
    409     def _on_model_load_complete(self, snap = True, skip_animation = False):
    410         if skip_animation or not self._live_preview:
    411             return
    412         mass = evetypes.GetMass(self.typeID)
    413         animDuration = GetAnimDuration(mass)
    414         self._live_preview.position_ship(self._get_camera_position(), animDuration, snap, get_sound_by_type_id(self.typeID), GetMassBasedSoundOffset(mass))
    415         self._set_dynamic_state(self._get_dynamic_state(), initial=snap)
    416         self._do_fade_in()
    417 
    418     def _update_caption(self):
    419         infoObject = u'%s (%s)' % (evetypes.GetName(self.typeID), shipgroup.get_ship_group_name(self.typeID))
    420         caption = localization.GetByLabel('UI/InfoWindow/InfoWindowCaption', infoObject=infoObject)
    421         self.SetCaption(caption)
    422 
    423     def GetPanelByTabType(self, tabType):
    424         pass
    425 
    426     def GetNeocomGroupLabel(self):
    427         return localization.GetByLabel('UI/InfoWindow/ShipInformationNeocomGroup')
    428 
    429     def GetHistoryData(self):
    430         return {'typeID': self.typeID,
    431          'itemID': self.itemID,
    432          'rec': self.rec,
    433          'parentID': self.parentID,
    434          'abstractinfo': self.abstractinfo,
    435          'selectTabType': self.GetSelectedTabIdx()}
    436 
    437     def GetCustomHeaderButtons(self):
    438         return [WindowMenuAction(window=self, icon=eveicon.navigate_forward, label=localization.GetByLabel('UI/Control/EveWindow/Next'), callback=self.OnForward, ui_name='ForwardButton', enabled_check=lambda window: window.history.IsForwardEnabled(), importance=WindowActionImportance.content), WindowMenuAction(window=self, icon=eveicon.navigate_back, label=localization.GetByLabel('UI/Control/EveWindow/Previous'), callback=self.OnBack, ui_name='BackButton', enabled_check=lambda window: window.history.IsBackEnabled(), importance=WindowActionImportance.content)]
    439 
    440     def GetMenuMoreOptions(self):
    441         menuData = super(ShipInfoWindow, self).GetMenuMoreOptions()
    442         if bool(session.role & ROLE_QA):
    443             menuData.AddEntry('QA: OPEN OLD WINDOW', self._qa_open_old_window)
    444         menuData.extend(GetMenuService().GetMenuFromItemIDTypeID(self.itemID, self.typeID, invItem=self.rec, includeMarketDetails=True).filter([localization.GetByLabel('UI/Commands/ShowInfo'), 'UI/Commands/ShowInfo']))
    445         return menuData
    446 
    447     def GetWindowLinkData(self):
    448         if not self._controller.type_id:
    449             return []
    450         return ShipDragData(typeID=self._controller.type_id, itemID=self._controller.item_id, ownerID=self._controller.owner_id)
    451 
    452     def OnBack(self, *args):
    453         self.UpdateHistoryData(self.GetHistoryData())
    454         infoWndData = self.history.GoBack()
    455         if infoWndData:
    456             self.ReconstructInfoWindow(branchHistory=False, **infoWndData)
    457 
    458     def OnForward(self, *args):
    459         self.UpdateHistoryData(self.GetHistoryData())
    460         infoWndData = self.history.GoForward()
    461         if infoWndData:
    462             self.ReconstructInfoWindow(branchHistory=False, **infoWndData)
    463 
    464     def get_visible_tabs(self, typeID, itemID = None, rec = None):
    465         ret = []
    466         for tabID, panelCls in SHIP_INFO_PANELS.iteritems():
    467             if panelCls.is_visible(typeID, itemID, rec):
    468                 ret.append(tabID)
    469 
    470         return ret
    471 
    472     def get_tab_class_by_type(self, tabID):
    473         if tabID in SHIP_INFO_PANELS:
    474             return SHIP_INFO_PANELS[tabID]
    475 
    476     def open_tab_by_id(self, tabID, params = None, initialize = False):
    477         panelCls = self.get_tab_class_by_type(tabID)
    478         if not panelCls:
    479             raise RuntimeError('No class found for tab type {tabID}'.format(tabID=tabID))
    480         if not initialize and not panelCls.is_visible(self.typeID, self.itemID, self.rec):
    481             return
    482         self._selectedTab = tabID
    483         if self._current_panel:
    484             self._current_panel.on_reset(initialize)
    485         self._last_tab_setting.set(tabID)
    486         self.mainContent.Flush()
    487         self.side_navigation.set_entry_selected(tabID)
    488         self._current_panel = panelCls(parent=self.mainContent, controller=self._controller, params=params, opacity=0)
    489         self._set_dynamic_state(self._get_dynamic_state())
    490         if self._dynamic_state == EXPANDED:
    491             self._current_panel.show_expanded_view()
    492         else:
    493             self._current_panel.show_minimized_view(self._dynamic_state == FULLY_MINIMIZED)
    494         animations.FadeIn(self._current_panel, duration=0.4)
    495         if tabID == TAB_SKINS and not self._live_preview:
    496             self._construct_live_preview(True)
    497             self._load_ship()
    498         self._update_preview_display_state()
    499         if self._live_preview and self._live_preview.display:
    500             self._live_preview.set_ship_state(tabID)
    501             mass = evetypes.GetMass(self.typeID)
    502             animDuration = GetAnimDuration(mass)
    503             self._live_preview.position_ship(self._get_camera_position(), animDuration, initialize, get_sound_by_type_id(self.typeID), GetMassBasedSoundOffset(mass))
    504             self._update_offset()
    505             self._update_zoom(duration=animDuration)
    506 
    507     def _get_skin_state(self):
    508         skin_state = self._skin_controller.previewed or self._skin_controller.pending or self._skin_controller.applied
    509         return skin_state
    510 
    511     def _change_skin(self):
    512         if not self._live_preview:
    513             return
    514         skin_state = self._get_skin_state()
    515         try:
    516             if skin_state == self._live_preview.skin_state:
    517                 return
    518         except:
    519             pass
    520 
    521         self._live_preview.preview_skin(self.typeID, subSystems=self._controller.get_subsystems(), skin_state=skin_state, skip_repositioning=True, snap=False, skip_animation=True)
    522         uthread2.Yield()
    523 
    524     def _reset_skin(self):
    525         if not self._live_preview:
    526             return
    527         self._skin_controller.SetPreviewed(None)
    528         self._change_skin()
    529 
    530     def _get_ship_length(self):
    531         if not self._live_preview:
    532             return 0
    533         return self._live_preview.get_bounding_sphere_radius() * 2
    534 
    535     def _get_camera_position(self):
    536         if self._current_panel is not None and (self._get_dynamic_state() == EXPANDED or self._selectedTab == TAB_SKINS):
    537             return self._current_panel.get_camera_position()
    538         return MINIMIZED_POSITION_OVERRIDE
    539 
    540     def _get_dynamic_state(self):
    541         _, _, width, _ = self.displayRect
    542         if width >= uicore.ScaleDpi(EXPANDED_WIDTH):
    543             if self.compact and not self._selectedTab == TAB_SKINS:
    544                 return MINIMIZED
    545             return EXPANDED
    546         return MINIMIZED
    547 
    548     def _set_dynamic_state(self, state, initial = False):
    549         if self._dynamic_state == state and not initial:
    550             return
    551         self.mainContent.padding = CONTENT_PADDING[state]
    552         is_compact = state == FULLY_MINIMIZED
    553         if state == EXPANDED:
    554             animations.FadeOut(self.bgFade, duration=0.4)
    555             if self._current_panel:
    556                 self._current_panel.show_expanded_view()
    557         elif state == MINIMIZED or is_compact:
    558             animations.FadeIn(self.bgFade, endVal=MINIMIZED_BG_OPACITY, duration=0.4)
    559             if self._current_panel:
    560                 self._current_panel.show_minimized_view(is_compact)
    561         self.split_view.set_minimized(is_compact)
    562         if is_compact:
    563             self.hasWindowIcon = False
    564             self.on_compact_mode_changed(self)
    565         else:
    566             self.hasWindowIcon = True
    567             self.on_compact_mode_changed(self)
    568         self._dynamic_state = state
    569         if self._live_preview:
    570             mass = evetypes.GetMass(self.typeID)
    571             animDuration = GetAnimDuration(mass)
    572             self._live_preview.position_ship(self._get_camera_position(), animDuration, initial, get_sound_by_type_id(self.typeID), GetMassBasedSoundOffset(mass))
    573             self._update_offset(initial)
    574             self._update_zoom(initial, animDuration)
    575         self._update_preview_display_state()
    576 
    577     def OnResizeUpdate(self, *args):
    578         state = self._get_dynamic_state()
    579         if state != self._dynamic_state:
    580             self._set_dynamic_state(state)
    581         self._update_offset()
    582 
    583     def _update_preview_display_state(self):
    584         display_preview = not self.compact
    585         if self.bgFade:
    586             self.bgFade.display = display_preview
    587         if not self._live_preview:
    588             return
    589         if self._selectedTab == TAB_SKINS:
    590             self.bgFade.display = False
    591             self._live_preview.display = True
    592             self.previewCont.pickState = PickState.ON
    593         else:
    594             self._live_preview.display = display_preview
    595             self.previewCont.pickState = PickState.ON if display_preview and self._dynamic_state == EXPANDED else PickState.OFF
    596 
    597     def _update_offset(self, initial = False):
    598         if self._current_panel is None or not self._live_preview:
    599             return
    600         centerOffset = 0
    601         verticalOffset = 0
    602         if self._dynamic_state == EXPANDED:
    603             leftIndent = self._current_panel.leftCont.GetAbsoluteSize()[0] / 2 + self.split_view.content.padLeft + self.mainContent.padLeft / 2 + 2
    604             previewCenter = self.GetAbsoluteSize()[0] / 2
    605             if previewCenter:
    606                 centerOffset = 1 - float(leftIndent) / previewCenter
    607             else:
    608                 centerOffset = 0
    609         elif self._selectedTab == TAB_SKINS:
    610             centerOffset = 0
    611         else:
    612             centerOffset = -1
    613         self._live_preview.centerOffset = centerOffset
    614         if self._selectedTab == TAB_SKINS and self._dynamic_state != EXPANDED:
    615             verticalOffset = -0.6
    616         self._live_preview.verticalOffset = verticalOffset
    617 
    618     def _update_zoom(self, initial = False, duration = 0):
    619         if self._dynamic_state == EXPANDED:
    620             if self._current_panel:
    621                 target_zoom = self._current_panel.get_zoom()
    622             else:
    623                 target_zoom = EXPANDED_ZOOM
    624         elif self._selectedTab == TAB_SKINS:
    625             target_zoom = 2
    626         else:
    627             target_zoom = MINIMIZED_ZOOM
    628         if initial:
    629             self._live_preview.SetZoom(target_zoom)
    630         else:
    631             self._live_preview.zoom_to(target_zoom, duration)
    632 
    633     def _on_stacked_changed_callback(self, *args):
    634         if self.stacked:
    635             self.split_view.padTop = 0
    636         else:
    637             self.split_view.padTop = 40
    638 
    639     def GetSelectedTabIdx(self):
    640         if self._current_panel:
    641             return self._current_panel.get_tab_type()
    642         return 0
    643 
    644     def _do_fade_in(self):
    645         if not self._initialized:
    646             return
    647         if not self._live_preview or not self._live_preview.loaded:
    648             return
    649         if self._black_bg.opacity:
    650             animations.FadeOut(self._black_bg, duration=0.8, timeOffset=0.1)
    651 
    652     def _qa_open_old_window(self, *args, **kwargs):
    653         from eve.client.script.ui.shared.info.infoWindow import InfoWindow
    654         import gametime
    655         InfoWindow.Open(windowInstanceID=gametime.GetWallclockTime(), typeID=self.typeID, itemID=self.itemID, rec=self.rec, parentID=self.parentID, abstractinfo=self.abstractinfo)
    656 
    657     def OnStartMinimize_(self, *args):
    658         if self._live_preview:
    659             self._live_preview.Hide()
    660 
    661     def OnStartMaximize_(self, *args):
    662         if self._live_preview:
    663             self._live_preview.Show()
    664 
    665 
    666 class ShipInfoSideNavigation(SideNavigation):
    667 
    668     def __init__(self, controller, is_expanded_func, expand_func = None, is_always_expanded_setting = None, **kwargs):
    669         super(ShipInfoSideNavigation, self).__init__(is_expanded_func, expand_func, is_always_expanded_setting, **kwargs)
    670         self._controller = controller
    671 
    672     def Flush(self):
    673         super(ShipInfoSideNavigation, self).Flush()
    674         self.bottomControls.Flush()
    675 
    676     def reconstruct(self, tabs, selectedTabID):
    677         self.Flush()
    678         typeID = self._controller.type_id
    679         if can_view_market_details(typeID):
    680             self.add_bottom_entry(texturePath=eveicon.market_details, text=localization.GetByLabel('UI/Inventory/ItemActions/ViewTypesMarketDetails'), on_click=self.on_market_button_click)
    681         if self._should_show_ship_tree_button():
    682             self.add_bottom_entry(texturePath=eveicon.ship_tree, text=localization.GetByLabel('UI/InfoWindow/ShowInISIS'), on_click=self.on_ship_tree_button_click)
    683         if TAB_FITTING in [ tab[0] for tab in tabs ]:
    684             if self._controller.is_owned_by_me and self._controller.is_assembled:
    685                 label = 'UI/Fitting/FittingWindow/SimulateShipFitting'
    686             else:
    687                 label = 'UI/Fitting/FittingWindow/SimulateShip'
    688             self.add_bottom_entry(texturePath=eveicon.simulate_fitting, text=localization.GetByLabel(label), on_click=self._on_simulate_button_clicked)
    689         self._add_top_entry()
    690         for tabID, panelCls in tabs:
    691             self.add_entry(entry_id=tabID, text=panelCls.get_name(), icon=panelCls.get_icon(), on_click=self._open_tab)
    692 
    693     def _open_tab(self, tab):
    694         self._controller.select_tab(tab.entry_id)
    695 
    696     def _layout_footer(self, main):
    697         super(ShipInfoSideNavigation, self)._layout_footer(main)
    698         self.bottomControls = ContainerAutoSize(parent=main, align=Align.TOBOTTOM)
    699 
    700     def _construct_background(self):
    701         self.panelBackground = Fill(bgParent=self, color=(0, 0, 0), opacity=0)
    702 
    703     def on_expanded_changed(self, expanded, animate = True):
    704         super(ShipInfoSideNavigation, self).on_expanded_changed(expanded, animate)
    705         duration = 0.2 if animate else 0
    706         if not expanded:
    707             animations.FadeOut(self.panelBackground, duration=duration)
    708         else:
    709             animations.FadeIn(self.panelBackground, endVal=0.9, duration=duration)
    710 
    711     def add_bottom_entry(self, texturePath, text, on_click, icon_size = 16):
    712         entry = SideNavigationEntry(icon=texturePath, text=text, on_click=on_click, icon_size=icon_size, parent=self.bottomControls, align=Align.TOBOTTOM, icon_color=TextColor.SECONDARY, force_compact=self._force_compact, on_hover=self._on_entry_hover)
    713         entry.on_expanded_changed(self.is_expanded_func(), animate=False)
    714         self._connect_to_expand(entry)
    715 
    716     def _add_top_entry(self):
    717         entry = TopSideNavigationEntry(parent=self._body, controller=self._controller, padBottom=4, on_hover=self._on_entry_hover, force_compact=self._force_compact)
    718         entry.on_expanded_changed(self.is_expanded_func(), animate=False)
    719         self._connect_to_expand(entry)
    720 
    721     def _on_entry_hover(self, entry):
    722         if not self.is_expanded_func() and self.expand_func is not None:
    723             self.expand_func(True)
    724 
    725     def _should_show_ship_tree_button(self):
    726         return sm.GetService('shipTree').IsInShipTree(self._controller.type_id)
    727 
    728     def on_ship_tree_button_click(self, *args):
    729         sm.GetService('shipTreeUI').OpenAndShowShip(self._controller.type_id)
    730 
    731     def on_market_button_click(self, *args):
    732         uthread.new(sm.StartService('marketutils').ShowMarketDetails, self._controller.type_id, None)
    733 
    734     def _on_simulate_button_clicked(self, *args, **kwargs):
    735         self._controller.open_simulation()
    736 
    737     def set_force_compact(self, force_compact):
    738         super(ShipInfoSideNavigation, self).set_force_compact(force_compact)
    739         for entry in self.bottomControls.children:
    740             if isinstance(entry, SideNavigationEntryInterface):
    741                 entry.set_force_compact(force_compact)
    742 
    743     def OnMouseEnter(self, *args):
    744         super(ShipInfoSideNavigation, self).OnMouseEnter(*args)
    745         if not self._force_compact:
    746             animations.FadeIn(self.panelBackground, endVal=0.9, duration=0.2)
    747 
    748     def OnMouseExit(self, *args):
    749         super(ShipInfoSideNavigation, self).OnMouseExit(*args)
    750         animations.FadeOut(self.panelBackground, duration=0.2)
    751 
    752 
    753 class TopSideNavigationEntry(Container, SideNavigationEntryInterface):
    754     default_state = uiconst.UI_NORMAL
    755     default_align = Align.TOTOP
    756     default_height = 40
    757     default_padRight = 10
    758     default_bgColor = eveThemeColor.THEME_FOCUSDARK
    759     isDragObject = True
    760     __notifyevents__ = ['OnSessionChanged']
    761 
    762     def __init__(self, controller, force_compact = False, on_hover = None, **kwargs):
    763         super(TopSideNavigationEntry, self).__init__(**kwargs)
    764         self.controller = controller
    765         self.character_id = self.controller.owner_id
    766         self._force_compact = force_compact
    767         self._on_hover = on_hover
    768         self._is_interactable = True
    769         sm.RegisterNotify(self)
    770         type_id = self.controller.type_id
    771         text = self.controller.get_owner_name() or self.controller.get_type_name()
    772         if self.character_id:
    773             subtext = ''
    774         else:
    775             subtext = self.controller.get_class_name()
    776             self._is_interactable = False
    777         self.bgFill.opacity = 0
    778         content = Container(parent=self, align=Align.TOALL, padding=(4, 2, 4, 2))
    779         icon_container = Container(parent=content, align=Align.CENTERLEFT, width=32, height=32)
    780         icon_container.pickState = PickState.OFF
    781         if self.character_id:
    782             self._construct_owner(self.controller.item_id, icon_container)
    783         else:
    784             self._consruct_type_icon(type_id, icon_container)
    785         self._text_container = Container(parent=content, align=Align.TOALL, padLeft=40, padRight=8, clipChildren=True, opacity=0)
    786         text_inner_container = ContainerAutoSize(parent=self._text_container, align=Align.VERTICALLY_CENTERED)
    787         self._text = TextBody(parent=text_inner_container, name='character_name_text', align=Align.TOTOP, text=text, maxLines=1, autoFadeSides=16, color=TextColor.SECONDARY)
    788         self._subtext = TextDetail(parent=text_inner_container, name='detail_text', align=Align.TOTOP, text=subtext, maxLines=1, autoFadeSides=16, color=TextColor.DISABLED)
    789         if self.character_id:
    790             uthread2.start_tasklet(self._update_owner_subtext)
    791 
    792     def _update_owner_subtext(self):
    793         owner_corporation_id = self.controller.owner_corporation_id
    794         if not owner_corporation_id:
    795             return
    796         corp_info = cfg.eveowners.Get(owner_corporation_id)
    797         if corp_info:
    798             self._subtext.text = corp_info.name
    799 
    800     def _construct_owner(self, ship_id, icon_container):
    801         icon = CharacterPortrait(parent=icon_container, align=Align.CENTER, size=32, character_id=self.character_id, textureSecondaryPath='res:/UI/Texture/circle_full.png', spriteEffect=trinity.TR2_SFX_MODULATE, pickState=PickState.OFF)
    802         self.OnClick = icon.OnClick
    803         self.GetMenu = icon.GetMenu
    804         self.GetDragData = icon.GetDragData
    805         if ship_id == session.shipid:
    806             Sprite(parent=icon_container, align=Align.CENTER, texturePath='res:/UI/Texture/classes/ShipInfo/circle_outer_34.png', height=34, width=34, color=eveColor.COPPER_OXIDE_GREEN)
    807             Frame(bgParent=self, align=Align.TOALL, texturePath='res:/UI/Texture/classes/ShipInfo/portrait_corner_frame_36.png', cornerSize=9, color=TextColor.SUCCESS, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.3, padding=2)
    808 
    809     def _consruct_type_icon(self, type_id, icon_container):
    810         type_icon = GetHoloIconPath(type_id) or GetIconFile(type_id)
    811         Sprite(parent=icon_container, align=Align.CENTER, texturePath=type_icon, height=32, width=32, blendMode=trinity.TR2_SBM_ADD, ignoreColorBlindMode=True)
    812         Sprite(parent=icon_container, align=Align.CENTER, texturePath='res:/UI/Texture/Shared/windowFrame.png', height=36, width=36, color=TextColor.DISABLED)
    813 
    814     def on_expanded_changed(self, expanded, animate = True):
    815         if expanded:
    816             self.set_expanded(animate)
    817         else:
    818             self.set_collapsed(animate)
    819 
    820     def set_expanded(self, animate = True):
    821         if animate:
    822             eveui.animation.fade_in(self._text_container, duration=0.2)
    823         else:
    824             self._text_container.opacity = 1.0
    825 
    826     def set_collapsed(self, animate = True):
    827         if animate:
    828             eveui.animation.fade_out(self._text_container, duration=0.2)
    829         else:
    830             self._text_container.opacity = 0.0
    831 
    832     def OnMouseEnter(self, *args):
    833         super(TopSideNavigationEntry, self).OnMouseEnter(*args)
    834         if self._is_interactable:
    835             eveui.Sound.button_hover.play()
    836             eveui.animation.fade_in(self.bgFill, end_value=0.15, duration=0.05)
    837         if self._on_hover:
    838             self._on_hover(self)
    839 
    840     def OnMouseExit(self, *args):
    841         super(TopSideNavigationEntry, self).OnMouseExit(*args)
    842         if self._is_interactable:
    843             eveui.animation.fade_out(self.bgFill, duration=0.2)
    844 
    845     def OnSessionChanged(self, _isRemote, _session, change):
    846         if not self.character_id or self.character_id != session.charid:
    847             return
    848         if 'corpid' in change:
    849             self._update_owner_subtext()
    850 
    851     def set_force_compact(self, force_compact):
    852         self._force_compact = force_compact
    853 
    854     def GetHint(self):
    855         if not self._force_compact:
    856             return ''
    857         title = self._text.text
    858         subtitle = self._subtext.text
    859         if subtitle:
    860             return u'<b>{}</b>\n{}'.format(title, subtitle)
    861         else:
    862             return title
    863 
    864     def GetTooltipPointer(self):
    865         return uiconst.POINT_RIGHT_2
    866 
    867     def GetTooltipDelay(self):
    868         if self._force_compact:
    869             return 0