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

skyhookWnd.py (28305B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\skyhook\skyhookWnd.py
      2 import carbonui.text.styles
      3 import datetimeutils
      4 import gametime
      5 import numbers
      6 import carbonui
      7 import eveicon
      8 import evetypes
      9 import uthread2
     10 from carbon.common.script.sys.serviceConst import ROLE_PROGRAMMER
     11 from carbon.common.script.util.timerstuff import AutoTimer
     12 from carbonui import TextColor, TextAlign
     13 from carbonui.button.group import ButtonGroup
     14 from carbonui.control.button import Button
     15 from carbonui.control.singlelineedits.singleLineEditInteger import SingleLineEditInteger
     16 from carbonui.control.window import Window
     17 import carbonui.uiconst as uiconst
     18 from carbonui.primitives.container import Container
     19 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     20 from carbonui.primitives.fill import Fill
     21 from carbonui.primitives.gridcontainer import GridContainer
     22 from carbonui.primitives.layoutGrid import LayoutGrid
     23 from carbonui.primitives.sprite import Sprite
     24 from const import MIN
     25 from eve.client.script.ui import eveColor, eveThemeColor
     26 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel
     27 from eve.client.script.ui.control.gauge import Gauge
     28 from eve.client.script.ui.control.gaugeCircular import GaugeCircular
     29 from eve.client.script.ui.control.itemIcon import ItemIcon
     30 from eve.client.script.ui.control.toggleSwitch import ToggleSwitch
     31 from eve.client.script.ui.shared.skyhook import NOT_AVAILABLE
     32 from eve.client.script.ui.shared.skyhook.uiController import CurrentSkyhookUiController
     33 from eve.common.script.util.eveFormat import FmtISK
     34 from eveexceptions import ExceptionEater
     35 from eveservices.menu import GetMenuService
     36 from eveui import animation
     37 from inventorycommon.typeHelpers import GetAveragePrice
     38 from localization import GetByLabel
     39 import logging
     40 from localization.formatters import FormatTimeIntervalShortWritten
     41 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     42 from sovereignty.skyhook.shared.skyhook_type_inference import is_reagent_skyhook
     43 from spacecomponents.client.components.orbitalSkyhook import IsCharacterAuthorisedToTakeFromSkyhook
     44 logger = logging.getLogger(__name__)
     45 
     46 def OpenSkyhookWindow(itemID):
     47     wnd = SkyhookWnd.GetIfOpen()
     48     if wnd and not wnd.destroyed:
     49         wnd.Maximize()
     50         if not wnd.dataController.IsSameItemID(itemID):
     51             wnd.LoadWindow(itemID)
     52     else:
     53         SkyhookWnd.Open(itemID=itemID)
     54 
     55 
     56 PRODUCING_THREAD = 'producingThread'
     57 ONLINE_STATE_THREAD = 'onlineStateThread'
     58 REAGENTS_QTY_THREAD = 'reagentsQtyThread'
     59 REAGENTS_TYPES_THREAD = 'reagentsTypesThread'
     60 
     61 class SkyhookWnd(Window):
     62     __guid__ = 'SkyhookWnd'
     63     default_minSize = [520, 250]
     64     default_windowID = 'obitalSkyhookWnd'
     65     default_captionLabelPath = 'UI/OrbitalSkyhook/OrbitalSkyhook'
     66     default_iconNum = 'res:/ui/Texture/WindowIcons/skyhook.png'
     67     __notifyevents__ = ['OnReagentsUpdated', 'OnSessionChanged']
     68 
     69     def DebugReload(self, *args):
     70         itemID = self.dataController.itemID
     71         self.Close()
     72         OpenSkyhookWindow(itemID)
     73 
     74     def ApplyAttributes(self, attributes):
     75         self.threadedLoader = ThreadedLoader()
     76         self._reagentsLoaded = False
     77         self.regentStorageLabel = None
     78         self.constructionDone = False
     79         self.progressThread = None
     80         super(SkyhookWnd, self).ApplyAttributes(attributes)
     81         itemID = attributes.itemID
     82         self.dataController = self.CreateDataController(itemID)
     83         self.product = None
     84         self.ConstructUI()
     85         self.LoadWindow(itemID)
     86 
     87     def CreateDataController(self, itemID):
     88         isAuthorisedToTake = IsCharacterAuthorisedToTakeFromSkyhook(itemID)
     89         controller = CurrentSkyhookUiController(itemID, isAuthorisedToTake)
     90         controller.on_linked_ship_updated.connect(self.OnLinkedShipChanged)
     91         return controller
     92 
     93     def ConstructUI(self):
     94         bottomPad = 8
     95         self.topSection = ContainerAutoSize(name='topSection', parent=self.content, align=carbonui.Align.TOTOP, alignMode=carbonui.Align.TOTOP, padBottom=bottomPad)
     96         padding = self.content_padding[0]
     97         self.topSection.bgFill = Fill(bgParent=self.topSection, color=eveThemeColor.THEME_FOCUSDARK, opacity=0.25, padding=(-padding,
     98          0,
     99          -padding,
    100          -bottomPad))
    101         topCont = ContainerAutoSize(parent=self.topSection, align=carbonui.Align.TOTOP, alignMode=carbonui.Align.TOTOP, top=6, padLeft=8, PadTop=4)
    102         self.buttonCont = ContainerAutoSize(parent=topCont, align=carbonui.Align.TORIGHT, left=8)
    103         self.onlineOfflineCont = OnlineOfflineCont(parent=self.buttonCont, dataController=self.dataController, align=carbonui.Align.TORIGHT, alignMode=uiconst.TOPLEFT)
    104         self.processingTimer = ProcessingTimer(parent=self.topSection, align=carbonui.Align.TOPRIGHT, alignMode=uiconst.TOPLEFT, dataController=self.dataController, pickState=carbonui.PickState.ON, top=40, left=8)
    105         self.nameLabel = carbonui.text.styles.TextHeadline(parent=topCont, align=carbonui.Align.TOTOP)
    106         self.ownerValueLabel, _ = self.ConstructLabelContainers(self.topSection, GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/Owner'), top=16)
    107         self.supplyingValueLabel, _ = self.ConstructLabelContainers(self.topSection, GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/Supplying'))
    108         self.producingValueLabel, _ = self.ConstructLabelContainers(self.topSection, GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/Producing'))
    109         self.producingRichnessSprite = Sprite(name='producingRichnessSprite', parent=self.producingValueLabel.parent, pos=(4, 0, 16, 16), align=carbonui.Align.CENTERLEFT, opacity=0.75)
    110         self.producingValueLabel.left = 20
    111         self.vulnerabilityValueLabel, self.vulnerabilityCont = self.ConstructLabelContainers(self.topSection, GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/VulnerabilityLabel'))
    112         self.vulnerabilityValueLabel.display = False
    113         self.vulnerabilityCont.display = False
    114         self.ConstructReagentSection()
    115         self.btnGroup = ButtonGroup(parent=self.content, align=uiconst.BOTTOMRIGHT, pos=(8, 8, 0, 0))
    116         self.takeBtn = Button(label=GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/TakeReagents', volume=0), func=self.TakeReagents, enabled=False)
    117         self.btnGroup.add_button(self.takeBtn)
    118         self.btnGroup.display = False
    119         self.constructionDone = True
    120 
    121     def ConstructLabelContainers(self, parent, headerText, labelClass = carbonui.text.styles.TextBody, textAlign = TextAlign.LEFT, top = 1):
    122         minLineHeight = 20
    123         lineCont = ContainerAutoSize(name='lineCont', parent=parent, align=carbonui.Align.TOTOP, alignMode=carbonui.Align.TOTOP, top=top, minHeight=minLineHeight, padLeft=8)
    124         headerCont = ContainerAutoSize(name='headerCont', parent=lineCont, align=carbonui.Align.TOLEFT)
    125         headerLabel = labelClass(name='headerLabel', parent=headerCont, text=headerText, align=carbonui.Align.TOPLEFT, color=TextColor.SECONDARY)
    126         rightLineCont = ContainerAutoSize(name='rightLineCont', parent=lineCont, align=carbonui.Align.TOTOP, alignMode=carbonui.Align.TOTOP, minHeight=minLineHeight)
    127         valueLabel = labelClass(name='valueLabel', parent=rightLineCont, text='', align=carbonui.Align.TOTOP, padLeft=4, state=uiconst.UI_NORMAL, textAlign=textAlign)
    128         loadingWheelAlign = carbonui.Align.CENTERRIGHT if textAlign == TextAlign.RIGHT else carbonui.Align.CENTERLEFT
    129         valueLabel.loadingWheel = LoadingWheel(parent=rightLineCont, align=loadingWheelAlign, pos=(8, 0, 16, 16), opacity=0.0)
    130         return (valueLabel, lineCont)
    131 
    132     def ConstructReagentSection(self):
    133         self.bayCont = ContainerAutoSize(name='bayCont', parent=self.content, align=carbonui.Align.TOTOP, alignMode=carbonui.Align.TOTOP, minHeight=16, padRight=8, padLeft=8)
    134         self.bayCont.loadingWheel = LoadingWheel(parent=self.bayCont, align=carbonui.Align.CENTERTOP, pos=(0, 0, 16, 16))
    135         self.typesCont = ContainerAutoSize(name='typesCont', parent=self.bayCont, align=carbonui.Align.TOTOP, alignMode=carbonui.Align.TOTOP, minHeight=16)
    136         self.bayCont.display = False
    137 
    138     def TakeReagents(self, *args):
    139         with self.takeBtn.busy_context:
    140             self.dataController.ExtractReagentsToShip()
    141 
    142     def UpdateTakeBtn(self, qty):
    143         self.dataController.selectedQty = qty
    144         volume = self.dataController.GetSelectedVolume()
    145         text = GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/TakeReagents', volume=volume)
    146         self.takeBtn.SetLabel(text)
    147         self.UpdateTakeBtnState()
    148 
    149     def UpdateTakeBtnState(self):
    150         self.takeBtn.enabled = self._reagentsLoaded and self.dataController.selectedQty > 0 and not self.dataController.linkedShipID
    151 
    152     def LoadWindow(self, itemID):
    153         if not self.dataController.IsSameItemID(itemID):
    154             with ExceptionEater('Failed disconnecting signal'):
    155                 self.dataController.on_linked_ship_updated.disconnect(self.OnLinkedShipChanged)
    156             self.dataController = self.CreateDataController(itemID)
    157         self.onlineOfflineCont.SetDataController(self.dataController)
    158         self.nameLabel.text = self.dataController.GetSkyhookName()
    159         self.ownerValueLabel.text = self.dataController.GetOwnerName()
    160         self.supplyingValueLabel.text = self.dataController.GetResourceDestName()
    161         self.threadedLoader.StartLoading(self.LoadProducing_thread)
    162         self.onlineOfflineCont.LoadCont()
    163         self.btnGroup.display = False
    164         self.bayCont.display = False
    165         if self.dataController.isAuthorisedToTake:
    166             self.threadedLoader.StartLoading(self.LoadAuthorized_thread)
    167 
    168     def LoadProducing_thread(self):
    169         animation.fade_in(self.producingValueLabel.loadingWheel)
    170         animation.fade_in(self.vulnerabilityValueLabel.loadingWheel)
    171         try:
    172             self.producingValueLabel.text = ''
    173             self.product = self.dataController.GetProduct()
    174             productName, texturePath, hintLabel, textHint = self.dataController.GetProductNameAndRichnessTexture()
    175             if self.destroyed:
    176                 return
    177             self.producingValueLabel.text = productName
    178             self.producingValueLabel.hint = textHint if textHint else ''
    179             self.producingValueLabel.left = 20 if texturePath else 0
    180             self.producingRichnessSprite.SetTexturePath(texturePath)
    181             self.producingRichnessSprite.hint = GetByLabel(hintLabel) if hintLabel else ''
    182             if is_reagent_skyhook(self.product):
    183                 self.vulnerabilityValueLabel.display = True
    184                 self.vulnerabilityCont.display = True
    185                 self.UpdateVulnerabilityTime()
    186             self.UpdateWindowSize()
    187         finally:
    188             animation.fade_out(self.producingValueLabel.loadingWheel, duration=0.1)
    189             animation.fade_out(self.vulnerabilityValueLabel.loadingWheel, duration=0.1)
    190 
    191     def LoadAuthorized_thread(self):
    192         onlineState = self.dataController.GetOnlineState()
    193         if not onlineState or onlineState == NOT_AVAILABLE:
    194             return
    195         reagentTypeID = self.dataController.GetPlanetReagentType()
    196         if reagentTypeID and reagentTypeID != NOT_AVAILABLE:
    197             self.btnGroup.display = True
    198             self.bayCont.display = True
    199             self.threadedLoader.StartLoading(self.LoadStorage_thread)
    200             self.UpdateWindowSize()
    201         else:
    202             self.bayCont.display = False
    203 
    204     def LoadStorage_thread(self):
    205         self._reagentsLoaded = False
    206         if not self.bayCont:
    207             return
    208         animation.fade_in(self.bayCont.loadingWheel)
    209         try:
    210             self.typesCont.Flush()
    211             typeID, secureAmount, insecureAmount = self.dataController.GetReagentsInSkyhook()
    212             secureMaxAmount, insecureMaxAmount = self.dataController.GetStorageQtyAndCapacity()
    213             if self.destroyed:
    214                 return
    215             if typeID is NOT_AVAILABLE:
    216                 carbonui.text.styles.TextBody(name='failureLabel', parent=self.typesCont, text=GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/NoDataAvailable'), align=carbonui.Align.TOTOP, color=TextColor.SECONDARY, textAlign=TextAlign.CENTER)
    217                 return
    218             storageGrid = GridContainer(name='storageGrid', parent=self.typesCont, align=carbonui.Align.TOTOP, alignMode=carbonui.Align.TOTOP, height=REAGENT_ICON_SIZE, contentSpacing=(16, 1), columns=2)
    219             StorageCont(parent=storageGrid, labelText=GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/SecureBay', units=secureAmount), typeID=typeID, amount=secureAmount, maxAmount=secureMaxAmount, top=10)
    220             StorageCont(parent=storageGrid, labelText=GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/SurplusBay', units=insecureAmount), typeID=typeID, amount=insecureAmount, maxAmount=insecureMaxAmount, top=10)
    221             ReagentTypeCont(parent=self.typesCont, typeID=typeID, amount=secureAmount, onEditChangeFunc=self.UpdateTakeBtn, top=10)
    222             self._reagentsLoaded = True
    223             self.UpdateTakeBtnState()
    224             self.UpdateWindowSize()
    225         finally:
    226             animation.fade_out(self.bayCont.loadingWheel, duration=0.1)
    227             self.progressThread = AutoTimer(500.0, self.UpdateProgress_thread)
    228 
    229     def UpdateProgress_thread(self):
    230         if self.destroyed:
    231             if self.progressThread:
    232                 self.progressThread.KillTimer()
    233             self.progressThread = None
    234             return
    235         if is_reagent_skyhook(self.product):
    236             self.UpdateVulnerabilityTime()
    237         nextTimestamp, amount, progress, isFull = self.dataController.GetNextReagentHarvestTimeAndAmount()
    238         if nextTimestamp == NOT_AVAILABLE and amount == NOT_AVAILABLE and progress == NOT_AVAILABLE and isFull is False:
    239             return
    240         timeRemaining = nextTimestamp - gametime.GetWallclockTime()
    241         self.processingTimer.SetValue(progress, timeRemaining)
    242 
    243     def _CalcTimeDeltaFromNowAndClampToOneMinute(self, time):
    244         now = gametime.GetWallclockTime()
    245         delta = time - now
    246         delta = max(MIN, delta)
    247         return delta
    248 
    249     def UpdateVulnerabilityTime(self):
    250         vulnerabiltyData = self.dataController.GetTheftVulnerabilityForSkyhook()
    251         if vulnerabiltyData is None:
    252             self.vulnerabilityValueLabel.text = GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/NoDataAvailable')
    253             return
    254         end_blue = datetimeutils.datetime_to_filetime(vulnerabiltyData.end)
    255         start_blue = datetimeutils.datetime_to_filetime(vulnerabiltyData.start)
    256         vulnerable = vulnerabiltyData.vulnerable
    257         if vulnerable:
    258             delta = self._CalcTimeDeltaFromNowAndClampToOneMinute(end_blue)
    259             countdown = FormatTimeIntervalShortWritten(delta, 'day', 'minute')
    260             vulnerabilityText = GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/VulnerableToTheft', countdown=countdown)
    261             color = eveColor.DANGER_RED
    262         else:
    263             delta = self._CalcTimeDeltaFromNowAndClampToOneMinute(start_blue)
    264             countdown = FormatTimeIntervalShortWritten(delta, 'day', 'minute')
    265             vulnerabilityText = GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/SecureFromTheft', countdown=countdown)
    266             self.vulnerabilityValueLabel.text = vulnerabilityText
    267             color = TextColor.NORMAL
    268         self.vulnerabilityValueLabel.text = vulnerabilityText
    269         self.vulnerabilityValueLabel.SetTextColor(color)
    270 
    271     def UpdateWindowSize(self):
    272         newHeight = self.sr.headerParent.height + self.content_padding[1] + self.content_padding[3] + 30
    273         newHeight += self.topSection.height
    274         if self.bayCont.display:
    275             newHeight += self.bayCont.height
    276         if self.btnGroup.display:
    277             newHeight += self.btnGroup.height
    278         self.SetMinSize([self.minsize[0], max(self.minsize[1], newHeight)])
    279 
    280     def OnReagentsUpdated(self, planetID, skyhookID):
    281         if skyhookID != self.dataController.itemID or not self.constructionDone:
    282             return
    283         self.LoadWindow(skyhookID)
    284 
    285     def OnLinkedShipChanged(self, linkItemID, linkedShipID):
    286         self.UpdateTakeBtnState()
    287 
    288     def OnSessionChanged(self, isRemote, session, change):
    289         if 'solarsystemid' in change or 'structureid' in change:
    290             self.Close()
    291 
    292     def Close(self, setClosed = False, *args, **kwds):
    293         with ExceptionEater('Failed disconnecting signal'):
    294             self.dataController.on_linked_ship_updated.disconnect(self.OnLinkedShipChanged)
    295             self.dataController = None
    296         super(SkyhookWnd, self).Close(setClosed=setClosed, *args, **kwds)
    297 
    298     def GetMenuMoreOptions(self):
    299         menu_data = super(SkyhookWnd, self).GetMenuMoreOptions()
    300         if session.role & ROLE_PROGRAMMER:
    301             menu_data.AddEntry('QA Reload', self.DebugReload)
    302         return menu_data
    303 
    304 
    305 class ThreadedLoader(object):
    306 
    307     def __init__(self):
    308         self.threadDict = {}
    309 
    310     def StartLoading(self, func):
    311         funcName = func.__name__
    312         existingThread = self.threadDict.pop(funcName, None)
    313         if existingThread:
    314             existingThread.kill()
    315         self.threadDict[funcName] = uthread2.start_tasklet(self._CallFunc_thread, func, funcName)
    316 
    317     def _CallFunc_thread(self, func, funcName):
    318         try:
    319             func()
    320         finally:
    321             self.threadDict.pop(funcName, None)
    322 
    323 
    324 REAGENT_ICON_SIZE = 64
    325 
    326 class StorageCont(ContainerAutoSize):
    327     default_align = carbonui.Align.TOTOP
    328     default_alignMode = carbonui.Align.TOTOP
    329     default_minHeight = REAGENT_ICON_SIZE
    330 
    331     def ApplyAttributes(self, attributes):
    332         super(StorageCont, self).ApplyAttributes(attributes)
    333         typeID = attributes.typeID
    334         amount = attributes.amount
    335         maxAmount = attributes.maxAmount
    336         labelText = attributes.labelText
    337         self.ConstructUI(typeID, labelText, amount, maxAmount)
    338 
    339     def ConstructUI(self, typeID, labelText, amount, maxAmount):
    340         self.storageLabel = carbonui.text.styles.TextBody(name='storageLabel', parent=self, text=labelText, align=carbonui.Align.TOTOP, color=TextColor.SECONDARY)
    341         self.gauge = Gauge(parent=self, color=eveThemeColor.THEME_FOCUS, align=uiconst.TOTOP, gaugeHeight=20, padLeft=0, padTop=8)
    342         self.gauge.SetValue(float(amount) / maxAmount if maxAmount != 0 else 1)
    343         self.gauge.SetValueText(GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/CurrentVolumeAndMax', maxVolume=maxAmount * evetypes.GetVolume(typeID), volume=amount * evetypes.GetVolume(typeID)))
    344 
    345 
    346 class ProcessingTimer(ContainerAutoSize):
    347 
    348     def ApplyAttributes(self, attributes):
    349         super(ProcessingTimer, self).ApplyAttributes(attributes)
    350         self.time_text = ''
    351         self.dataController = attributes.dataController
    352         self.ConstructUI()
    353 
    354     def ConstructUI(self):
    355         self.gaugeCont = ContainerAutoSize(name='gaugeCont', parent=self, pickState=carbonui.PickState.OFF)
    356         self.gauge = GaugeCircular(parent=self.gaugeCont, colorStart=eveThemeColor.THEME_FOCUS, colorEnd=eveThemeColor.THEME_FOCUS, align=carbonui.Align.CENTER, radius=20, showMarker=False, glowBrightness=0.8)
    357         self.gauge.display = False
    358         self.icon = Sprite(parent=self.gaugeCont, texturePath=eveicon.inventory, align=carbonui.Align.CENTER, width=16, height=16, opacity=0.75, state=uiconst.UI_NORMAL)
    359         self.icon.display = False
    360         self.loadingWheel = LoadingWheel(parent=self, align=carbonui.Align.CENTER, pos=(0, 4, 16, 16), opacity=0.0)
    361         animation.fade_in(self.loadingWheel)
    362         self.storageLabel = carbonui.text.styles.TextDetail(name='storageLabel', parent=self, text='', color=TextColor.SECONDARY, align=carbonui.Align.CENTER, top=30)
    363 
    364     def SetValue(self, value, time_left):
    365         self.gauge.SetValue(value, False)
    366         self.time_text = FormatTimeIntervalShortWritten(time_left, showFrom='hour', showTo='second')
    367         self.storageLabel.text = self.time_text
    368         self.gauge.display = True
    369         self.icon.display = True
    370         animation.fade_out(self.loadingWheel)
    371 
    372     def LoadTooltipPanel(self, tooltipPanel, *args):
    373         tooltipPanel.LoadStandardSpacing()
    374         securedAmount, unsecuredAmount = self.dataController.GetNextReagentHarvestAmounts()
    375         securedText = GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/SecureBay', units=securedAmount)
    376         surplusText = GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/SurplusBay', units=unsecuredAmount)
    377         tooltipPanel.AddLabelMedium(text=GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/NextOutput', countdown=self.time_text))
    378         tooltipPanel.AddLabelMedium(text=securedText, align=carbonui.Align.TOPLEFT)
    379         tooltipPanel.AddLabelMedium(text=surplusText, align=carbonui.Align.TOPLEFT)
    380 
    381 
    382 class ReagentTypeCont(ContainerAutoSize):
    383     default_align = carbonui.Align.TOTOP
    384     default_alignMode = carbonui.Align.TOTOP
    385     default_minHeight = REAGENT_ICON_SIZE
    386 
    387     def ApplyAttributes(self, attributes):
    388         super(ReagentTypeCont, self).ApplyAttributes(attributes)
    389         self.onEditChangeFunc = attributes.onEditChangeFunc
    390         typeID = attributes.typeID
    391         amount = attributes.amount
    392         self.ContructUI()
    393         self.LoadType(typeID, amount)
    394 
    395     def ContructUI(self):
    396         spriteCont = Container(name='spriteCont', parent=self, align=carbonui.Align.TOLEFT, width=REAGENT_ICON_SIZE)
    397         self.reagentSprite = ItemIcon(name='reagentSprite', parent=spriteCont, align=carbonui.Align.CENTERLEFT, pos=(0,
    398          0,
    399          REAGENT_ICON_SIZE,
    400          REAGENT_ICON_SIZE), state=uiconst.UI_NORMAL)
    401         amountCont = ContainerAutoSize(name='amountCont', parent=self, align=carbonui.Align.TORIGHT, minHeight=REAGENT_ICON_SIZE)
    402         restCont = ContainerAutoSize(name='restCont', parent=self, align=carbonui.Align.TOTOP, alignMode=carbonui.Align.TOTOP, padLeft=10)
    403         self.typeNameLabel = carbonui.text.styles.TextBody(name='valueLabel', parent=restCont, text='', align=carbonui.Align.TOTOP)
    404         self.secureLabel = carbonui.text.styles.TextDetail(name='secureLabel', parent=restCont, text=GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/ReagentsAvailable'), align=carbonui.Align.TOTOP, top=10, color=TextColor.SECONDARY)
    405         self.secureValueLabel = carbonui.text.styles.TextBody(name='secureValueLabel', parent=restCont, text='', align=carbonui.Align.TOTOP)
    406         self.amountEdit = SingleLineEditInteger(name='amountEdit', parent=amountCont, align=uiconst.CENTERRIGHT, width=100, OnChange=self.OnEditChanged)
    407 
    408     def OnEditChanged(self, *args):
    409         self.onEditChangeFunc(self.amountEdit.GetValue())
    410 
    411     def LoadType(self, typeID, amount):
    412         self.reagentSprite.SetTypeID(typeID)
    413         self.typeNameLabel.text = evetypes.GetName(typeID)
    414         self.amountEdit.SetValue(amount)
    415         self.amountEdit.SetMaxValue(amount)
    416         averagePrice = GetAveragePrice(typeID)
    417         if averagePrice is None:
    418             averagePrice = 0
    419         self.secureValueLabel.text = GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/ProcessedUnits', units=amount, price=FmtISK(averagePrice))
    420         self.reagentSprite.GetMenu = lambda *args: GetMenuService().GetMenuFromItemIDTypeID(None, typeID)
    421         self.reagentSprite.OnClick = lambda *args: sm.GetService('info').ShowInfo(typeID=typeID)
    422 
    423 
    424 class OnlineOfflineCont(ContainerAutoSize):
    425 
    426     def ApplyAttributes(self, attributes):
    427         self.toggleSwitch = None
    428         self.threadedLoader = ThreadedLoader()
    429         super(OnlineOfflineCont, self).ApplyAttributes(attributes)
    430         self._dataController = attributes.dataController
    431         self.unknowStateLabel = carbonui.text.styles.TextDetail(name='unknowStateLabel', parent=self, text=GetByLabel('UI/OrbitalSkyhook/SkyhookWnd/UnknownOnlineState'), align=carbonui.Align.TOPLEFT)
    432         self.unknowStateLabel.display = False
    433         self.loadingWheel = LoadingWheel(parent=self, align=carbonui.Align.TOPLEFT, pos=(8, 0, 16, 16), opacity=0.0)
    434         self.onlineBtnGrid = LayoutGrid(parent=self, cellSpacing=3, top=8)
    435 
    436     def LoadCont(self):
    437         self.threadedLoader.StartLoading(self.LoadOnlineState_thread)
    438 
    439     def LoadOnlineState_thread(self):
    440         animation.fade_in(self.loadingWheel)
    441         try:
    442             self.onlineBtnGrid.Flush()
    443             self.unknowStateLabel.display = False
    444             onlineState = self._dataController.GetOnlineState()
    445             if onlineState == NOT_AVAILABLE:
    446                 self.unknowStateLabel.display = True
    447             elif self.CanEditSkyhookOnlineState(onlineState):
    448                 self.AddToggleButtons(onlineState)
    449             else:
    450                 self.ReloadReadOnlyStatus(onlineState)
    451         finally:
    452             animation.fade_out(self.loadingWheel, duration=0.1)
    453 
    454     def CanEditSkyhookOnlineState(self, onlineState):
    455         if not self._dataController.HasConfigRoles():
    456             return False
    457         if not onlineState:
    458             return True
    459         reagentTypeID = self._dataController.GetPlanetReagentType()
    460         if reagentTypeID > 0:
    461             return False
    462         return True
    463 
    464     def AddToggleButtons(self, onlineState):
    465         self.toggleSwitch = ToggleSwitch(parent=self.onlineBtnGrid, align=carbonui.Align.TOPLEFT, adjustSize=True, checked=bool(onlineState), onText=GetByLabel('UI/SystemMenu/DisplayAndGraphics/Common/On').upper(), offText=GetByLabel('UI/SystemMenu/DisplayAndGraphics/Common/Off').upper(), confirmFunc=self.ConfirmOnlineStateChange)
    466         self.toggleSwitch.GetHint = self.GetToggleSwitchHint
    467         self.toggleSwitch.on_switch_changed.connect(self.ChangeOnlineState)
    468 
    469     def UpdateToggleSwitch(self):
    470         onlineState = self._dataController.GetOnlineState()
    471         self.toggleSwitch.SetValue(onlineState, report=False)
    472 
    473     def SetDataController(self, dataController):
    474         self._dataController = dataController
    475 
    476     def ReloadReadOnlyStatus(self, onlineState):
    477         onlineState = self._dataController.GetOnlineState()
    478         if onlineState == NOT_AVAILABLE:
    479             self.unknowStateLabel.display = True
    480         if onlineState:
    481             text = GetByLabel('UI/Common/Online')
    482             iconColor = eveColor.SUCCESS_GREEN
    483             hint = ''
    484         else:
    485             text = GetByLabel('UI/Common/Offline')
    486             iconColor = eveColor.DANGER_RED
    487             hint = GetByLabel('Tooltips/StructureUI/SkyhookIsOfflineNonOwner')
    488         STATUS_ICON_SIZE = 14
    489         onlineStateIcon = Sprite(name='onlineStateIcon', parent=self.onlineBtnGrid, align=carbonui.Align.CENTERLEFT, pos=(0,
    490          0,
    491          STATUS_ICON_SIZE,
    492          STATUS_ICON_SIZE), state=uiconst.UI_NORMAL, texturePath='res:/UI/Texture/classes/Contacts/onlineIcon.png', outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.8, hint=hint)
    493         onlineStateIcon.SetRGBA(*iconColor)
    494         label = carbonui.text.styles.TextDetail(parent=self.onlineBtnGrid, text=text, align=carbonui.Align.CENTERLEFT, state=uiconst.UI_NORMAL)
    495         label.hint = hint
    496 
    497     def ChangeOnlineState(self, toggleSwitch, onlineState):
    498         with toggleSwitch.busy_context:
    499             if onlineState:
    500                 self._dataController.SetOnline()
    501             else:
    502                 self._dataController.SetOffline()
    503             self.UpdateToggleSwitch()
    504 
    505     def GetToggleSwitchHint(self):
    506         onlineState = self._dataController.GetOnlineState()
    507         if onlineState == NOT_AVAILABLE:
    508             return ''
    509         if onlineState:
    510             labelPath = 'Tooltips/StructureUI/SkyhookTurnOffline'
    511         else:
    512             labelPath = 'Tooltips/StructureUI/SkyhookTurnOnline'
    513         return GetByLabel(labelPath)
    514 
    515     def ConfirmOnlineStateChange(self, currentState):
    516         if not currentState:
    517             return True
    518         if isinstance(self._dataController.GetProduct(), numbers.Number):
    519             if prompt_player_fsd_dialog('SkyhookOfflineConfirmReagent', {}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES:
    520                 return False
    521         elif prompt_player_fsd_dialog('SkyhookOfflineConfirmResources', {}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES:
    522             return False
    523         return True
    524 
    525     def Close(self):
    526         with ExceptionEater('Closing OnlineOfflineCont'):
    527             if self.toggleSwitch:
    528                 self.toggleSwitch.on_switch_changed.connect(self.ChangeOnlineState)
    529             self.dataController = None
    530         super(OnlineOfflineCont, self).Close()