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

shipSafetyButton.py (28816B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\inflight\shipSafetyButton.py
      2 from math import pi, radians, sqrt
      3 import blue
      4 import carbonui.const as uiconst
      5 import eveicon
      6 import localization
      7 import trinity
      8 import uthread
      9 from carbon.client.script.environment.AudioUtil import PlaySound
     10 from carbonui import fontconst, PickState, Align, TextColor
     11 from carbonui.primitives.container import Container
     12 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     13 from carbonui.primitives.fill import Fill
     14 from carbonui.primitives.frame import Frame
     15 from carbonui.primitives.layoutGrid import LayoutGrid
     16 from carbonui.primitives.sprite import Sprite
     17 from carbonui.primitives.transform import Transform
     18 from carbonui.primitives.vectorlinetrace import VectorLineTrace
     19 from carbonui.text import styles
     20 from carbonui.text.styles import TextBody
     21 from carbonui.uicore import uicore
     22 from carbonui.util.color import Color
     23 from eve.client.script.ui.control import eveLabel
     24 from eve.client.script.ui.control.themeColored import FillThemeColored
     25 from eve.client.script.ui.crimewatch import crimewatchConst
     26 from eve.client.script.ui.inflight import shipSafetyConst
     27 from eve.client.script.ui.inflight.shipSafetyConst import OPACITY_BG, DOCK_POINTER_LENGTH, DOCK_MARGIN, DESELECTED_BUTTON_OPACITY, HINT_DELAY, TRANSITION_TOTAL_DURATION, TRANSITION_STAGE2_ROTATION_DEG, TRANSITION_STAGE3_ROTATION_DEG
     28 from eve.client.script.ui.shared.mapView.links.utils import get_map_link
     29 from eve.client.script.ui.shared.maps.mapcommon import STARMODE_SECURITY
     30 from eve.client.script.util.settings import IsShipHudTopAligned
     31 from eve.common.lib import appConst as const
     32 from localization import GetByLabel
     33 from security.client.const import get_range_id_by_security_status, SAFE_SEC
     34 from security.client.securityColor import get_security_status_color
     35 from signals import Signal
     36 
     37 class LightEmittingDiode(Container):
     38     __guid__ = 'shipSafetyButton.LightEmittingDiode'
     39     default_color = (1, 0, 0, 1)
     40     default_align = uiconst.TOPLEFT
     41     default_width = 32
     42     default_height = 32
     43 
     44     def ApplyAttributes(self, attributes):
     45         Container.ApplyAttributes(self, attributes)
     46         self.isAnimating = False
     47         self.colorSprite = Sprite(bgParent=self, texturePath='res:/UI/Texture/CharacterCreation/radiobuttonColor.dds')
     48         Sprite(bgParent=self, texturePath='res:/UI/Texture/CharacterCreation/radiobuttonBack.dds', opacity=0.4)
     49         Sprite(bgParent=self, texturePath='res:/UI/Texture/CharacterCreation/radiobuttonShadow.dds', color=(0.4, 0.4, 0.4, 0.4))
     50         self.SetColor(attributes.get('color', self.default_color))
     51 
     52     def SetColor(self, color):
     53         self._color = color
     54         self.colorSprite.SetRGBA(*color)
     55 
     56     def Blink(self, cycles = 2, audioAlert = True):
     57         if not self.isAnimating:
     58             self.isAnimating = True
     59             self.blinkCurve = uicore.animations.SpColorMorphTo(self.colorSprite, Color.BLACK, self._color, duration=0.5, loops=cycles, curveType=uiconst.ANIM_BOUNCE, callback=self._EndBlink, sleep=False)
     60             if audioAlert:
     61                 sm.GetService('audio').SendUIEvent('crimewatch_locked_play')
     62 
     63     def _EndBlink(self):
     64         self.isAnimating = False
     65         self.blinkCurve.Stop()
     66         self.blinkCurve = None
     67         self.SetColor(self._color)
     68 
     69 
     70 class SafetyTransitionEffect(Container):
     71     default_align = uiconst.CENTER
     72     default_width = 26
     73     default_height = 26
     74     default_state = uiconst.UI_DISABLED
     75     TEXTURE_STAGE1 = 'res:/UI/Texture/classes/ShipUI/secTrans00.png'
     76     TEXTURE_STAGE2 = 'res:/UI/Texture/classes/ShipUI/secTrans01.png'
     77     TEXTURE_STAGE3 = 'res:/UI/Texture/classes/ShipUI/secTrans02.png'
     78     TEXTURE_STAGE4 = 'res:/UI/Texture/classes/ShipUI/secTrans03.png'
     79 
     80     def ApplyAttributes(self, attributes):
     81         super(SafetyTransitionEffect, self).ApplyAttributes(attributes)
     82         green = crimewatchConst.Colors.Green.GetRGBA()
     83         self.transform0 = Transform(parent=self, name='transEffect0', align=uiconst.TOALL, rotationCenter=(0.5, 0.5))
     84         self.sprite0 = Sprite(parent=self.transform0, name='secTrans0', texturePath=self.TEXTURE_STAGE1, align=uiconst.TOALL, state=uiconst.UI_DISABLED, color=green, blendMode=trinity.TR2_SBM_ADD, opacity=0.0)
     85         self.transform1 = Transform(parent=self, name='transEffect1', align=uiconst.TOALL, rotationCenter=(0.5, 0.5))
     86         self.sprite1 = Sprite(parent=self.transform1, name='secTrans1', texturePath=self.TEXTURE_STAGE2, align=uiconst.CENTER, width=self.width + 2, height=self.height + 2, state=uiconst.UI_DISABLED, color=green, blendMode=trinity.TR2_SBM_ADD, opacity=0.0)
     87         self.transform2 = Transform(parent=self, name='transEffect2', align=uiconst.TOALL, rotationCenter=(0.5, 0.5))
     88         self.sprite2 = Sprite(parent=self.transform2, name='secTrans2', texturePath=self.TEXTURE_STAGE3, align=uiconst.CENTER, width=self.width + 2, height=self.height + 2, state=uiconst.UI_DISABLED, color=green, blendMode=trinity.TR2_SBM_ADD, opacity=0.0)
     89         self.transform2_inner = Transform(parent=self, name='transEffect2Inner', align=uiconst.TOALL, rotationCenter=(0.5, 0.5))
     90         self.sprite2_inner = Sprite(parent=self.transform2_inner, name='secTrans2Inner', texturePath=self.TEXTURE_STAGE4, align=uiconst.CENTER, width=self.width + 2, height=self.height + 2, state=uiconst.UI_DISABLED, color=green, blendMode=trinity.TR2_SBM_ADD, opacity=0.0)
     91 
     92     def play_transition(self):
     93         uthread.new(self._animate_transition)
     94 
     95     def _animate_transition(self):
     96         rot2 = radians(45)
     97         rot3 = radians(45)
     98         self.transform0.SetRotation(0)
     99         self.transform1.SetRotation(0)
    100         self.transform2.SetRotation(0)
    101         self.sprite0.opacity = 0.0
    102         self.sprite1.opacity = 0.0
    103         self.sprite2.opacity = 0.0
    104         self.sprite2_inner.opacity = 0.0
    105         blue.pyos.synchro.SleepSim(200)
    106         if self.destroyed:
    107             return
    108         uicore.animations.FadeTo(self.sprite0, startVal=0.0, endVal=0.6, duration=0.2, sleep=True)
    109         if self.destroyed:
    110             return
    111         blue.pyos.synchro.SleepSim(200)
    112         if self.destroyed:
    113             return
    114         uicore.animations.FadeTo(self.sprite0, startVal=0.6, endVal=0.0, duration=0.2)
    115         uicore.animations.Tr2DRotateTo(self.transform1, startAngle=0.0, endAngle=rot2, duration=0.2)
    116         uicore.animations.FadeTo(self.sprite1, startVal=0.0, endVal=1.0, duration=0.2, sleep=True)
    117         if self.destroyed:
    118             return
    119         uicore.animations.FadeTo(self.sprite1, startVal=1.0, endVal=0.5, duration=0.1, sleep=True)
    120         if self.destroyed:
    121             return
    122         blue.pyos.synchro.SleepSim(300)
    123         if self.destroyed:
    124             return
    125         uicore.animations.FadeTo(self.sprite2, startVal=0.0, endVal=1.0, duration=0.3)
    126         uicore.animations.Tr2DRotateTo(self.transform2, startAngle=rot3, endAngle=0.0, duration=0.3, sleep=True)
    127         if self.destroyed:
    128             return
    129         blue.pyos.synchro.SleepSim(300)
    130         if self.destroyed:
    131             return
    132         uicore.animations.FadeTo(self.sprite2_inner, startVal=0.0, endVal=1.0, duration=0.2, sleep=True)
    133 
    134     def show_static(self):
    135         self.sprite0.opacity = 0.0
    136         self.sprite1.opacity = 0.5
    137         self.transform1.SetRotation(radians(TRANSITION_STAGE2_ROTATION_DEG))
    138         self.sprite2.opacity = 1.0
    139         self.transform2.SetRotation(radians(TRANSITION_STAGE3_ROTATION_DEG))
    140         self.sprite2_inner.opacity = 1.0
    141         self.transform2_inner.SetRotation(radians(TRANSITION_STAGE3_ROTATION_DEG))
    142 
    143     def clear(self, animate = True):
    144         if animate and (self.sprite0.opacity > 0.0 or self.sprite1.opacity > 0.0 or self.sprite2.opacity > 0.0 or self.sprite2_inner.opacity > 0.0):
    145             curveSet = uicore.animations.FadeOut(self.sprite0, duration=0.3)
    146             uicore.animations.FadeOut(self.sprite1, duration=0.3, curveSet=curveSet)
    147             uicore.animations.FadeOut(self.sprite2, duration=0.3, curveSet=curveSet)
    148             uicore.animations.FadeOut(self.sprite2_inner, duration=0.3, curveSet=curveSet)
    149         else:
    150             self.sprite0.opacity = 0.0
    151             self.sprite1.opacity = 0.0
    152             self.sprite2.opacity = 0.0
    153             self.sprite2_inner.opacity = 0.0
    154 
    155 
    156 class SafetyButton(Container):
    157     default_align = uiconst.TOPLEFT
    158     default_width = 24
    159     default_height = 24
    160     default_state = uiconst.UI_NORMAL
    161     default_hintDelay = HINT_DELAY
    162     __notifyevents__ = ['OnSafetyLevelChanged', 'OnCrimewatchSafetyCheckFailed', 'OnSessionChanged']
    163 
    164     def ApplyAttributes(self, attributes):
    165         super(SafetyButton, self).ApplyAttributes(attributes)
    166         sm.RegisterNotify(self)
    167         self.selector = None
    168         self.content = Container(parent=self, name='content', align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, pos=(0, 0, 24, 24))
    169         self.hilite = Sprite(parent=self.content, name='hilite', align=uiconst.TOALL, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/ShipUI/utilBtnBaseAndShadow.png', color=(0.63, 0.63, 0.63, 1.0), blendMode=trinity.TR2_SBM_ADD, scale=(0.75, 0.75), display=False)
    170         Sprite(parent=self.content, name='baseButton', align=uiconst.TOALL, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/ShipUI/utilBtnBaseAndShadow.png', scale=(0.75, 0.75))
    171         Sprite(parent=self, name='busy', align=uiconst.TOALL, state=uiconst.UI_DISABLED, texturePath='res:/UI/Texture/classes/ShipUI/utilBtnGlow.png', color=(0, 0, 0, 0.8))
    172         self.led = LightEmittingDiode(parent=self.content, align=uiconst.CENTER)
    173         self.transitionEffect = SafetyTransitionEffect(parent=self)
    174         self.update_safety_level()
    175         self._update_starter_space_effect(animate=True)
    176 
    177     def can_be_modified(self):
    178         return not sm.GetService('crimewatchSvc').IsSafetyLockedToFullLevel()
    179 
    180     def update_safety_level(self):
    181         safetyLevel = sm.GetService('crimewatchSvc').GetSafetyLevel()
    182         self.SetSafetyLevel(safetyLevel, doAlert=False)
    183 
    184     def SetSafetyLevel(self, safetyLevel, doAlert = True):
    185         color = shipSafetyConst.COLOR[safetyLevel]
    186         self.led.SetColor(color.GetRGBA())
    187         if doAlert:
    188             audioEvent = shipSafetyConst.AUDIO_EVENT[safetyLevel]
    189             sm.GetService('audio').SendUIEvent(audioEvent)
    190 
    191     def GetHint(self):
    192         if self.selector:
    193             return None
    194         safety_level = sm.GetService('crimewatchSvc').GetSafetyLevel()
    195         color = shipSafetyConst.COLOR[safety_level]
    196         hint = GetByLabel(shipSafetyConst.STATE_TEXT[safety_level], color=color.GetHex())
    197         locked_to_full_reason = sm.GetService('crimewatchSvc').IsSafetyLockedToFullLevel()
    198         if locked_to_full_reason:
    199             hint += '\n\n' + locked_to_full_reason
    200         button_hint = shipSafetyConst.STATE_HINT[safety_level]
    201         hint += '\n\n' + GetByLabel(button_hint, color=color.GetHex())
    202         return hint
    203 
    204     def OnMouseEnter(self):
    205         PlaySound(uiconst.SOUND_BUTTON_HOVER)
    206         self.hilite.display = True
    207 
    208     def OnMouseExit(self):
    209         self.hilite.display = False
    210 
    211     def OnClick(self):
    212         if self.selector:
    213             return
    214         PlaySound(uiconst.SOUND_EXPAND)
    215         self.selector = SafetyLevelSelector(parent=uicore.layer.abovemain, anchor=self, width=280 * fontconst.fontSizeFactor)
    216         self.selector.on_closed.connect(self.on_selector_closed)
    217 
    218     def on_selector_closed(self):
    219         self.selector = None
    220 
    221     def OnMouseDown(self, btn, *args):
    222         self.content.top = 1
    223 
    224     def OnMouseUp(self, *args):
    225         self.content.top = 0
    226 
    227     def OnSafetyLevelChanged(self, safetyLevel):
    228         self.SetSafetyLevel(safetyLevel)
    229         if self.selector is None or self.selector.destroyed:
    230             return
    231         self.selector.state = uiconst.UI_DISABLED
    232         while self.selector.confirmationButton and not self.selector.confirmationButton.destroyed:
    233             blue.pyos.synchro.SleepSim(25)
    234 
    235         self.selector.SetSafetyLevel(safetyLevel)
    236         blue.pyos.synchro.SleepSim(500)
    237         if self.selector:
    238             self.selector.CloseSelector()
    239         self.led.Blink(audioAlert=False)
    240 
    241     def OnCrimewatchSafetyCheckFailed(self):
    242         self.led.Blink()
    243 
    244     def _update_starter_space_effect(self, animate = False):
    245         if sm.GetService('crimewatchSvc').IsSafetyLockedToFullLevel():
    246             if animate:
    247                 self.transitionEffect.play_transition()
    248             else:
    249                 self.transitionEffect.show_static()
    250         else:
    251             self.transitionEffect.clear(animate=animate)
    252 
    253     def OnSessionChanged(self, isRemote, sess, change):
    254         if 'solarsystemid' not in change:
    255             return
    256         self._update_starter_space_effect(animate=True)
    257 
    258 
    259 class SafetyLevelSelector(ContainerAutoSize):
    260     __guid__ = 'shipSafetyButton.SafetyLevelSelector'
    261     default_width = 250
    262     default_state = uiconst.UI_NORMAL
    263     default_align = uiconst.TOPLEFT
    264     default_alignMode = uiconst.TOTOP
    265     default_name = 'SafetyLevelSelector'
    266 
    267     def __init__(self, anchor, **kwargs):
    268         super(SafetyLevelSelector, self).__init__(**kwargs)
    269         currentSafetyLevel = sm.GetService('crimewatchSvc').GetSafetyLevel()
    270         self.isTabStop = True
    271         self.confirmationButton = None
    272         self.anchor = anchor
    273         self.on_closed = Signal('on_closed')
    274         self.lineTrace = None
    275         self.construct_layout(currentSafetyLevel)
    276         self.on_size_changed.connect(self._on_size_changed)
    277         self.anim_entry()
    278 
    279     def _on_size_changed(self, width, height):
    280         align_to_top = IsShipHudTopAligned()
    281         self.SetPosition(align_to_top)
    282         self.DrawFrame(align_to_top)
    283         self.DrawPointer(align_to_top)
    284 
    285     def construct_layout(self, currentSafetyLevel):
    286         self.construct_text()
    287         self.construct_buttons(currentSafetyLevel)
    288         self.construct_bg()
    289 
    290     def construct_bg(self):
    291         FillThemeColored(bgParent=self, opacity=OPACITY_BG)
    292 
    293     def anim_entry(self):
    294         self.opacity = 0.0
    295         uicore.animations.FadeIn(self, duration=0.1, loops=1)
    296         uicore.registry.SetFocus(self)
    297 
    298     def update_height(self):
    299         height = 0
    300         for container in self.children:
    301             if container.GetAlign() == uiconst.TOTOP:
    302                 height += container.height
    303 
    304         self.height = height
    305 
    306     def construct_text(self):
    307         textCont = LayoutGrid(parent=self, align=uiconst.TOTOP, name='textCont', columns=1, cellSpacing=8, margin=16)
    308         styles.TextHeader(parent=textCont, text=localization.GetByLabel('UI/Crimewatch/SafetyLevel/SafetySettingsCaption'))
    309         bodyText = localization.GetByLabel('UI/Crimewatch/SafetyLevel/SafetyLevelSelectionBody', suspectColor=crimewatchConst.Colors.Yellow.GetHex(), criminalColor=crimewatchConst.Colors.Red.GetHex())
    310         styles.TextBody(parent=textCont, text=bodyText, maxWidth=self.width - 32)
    311         security_status = sm.GetService('map').GetSystemSecurityValue(session.solarsystemid2)
    312         security_level = get_range_id_by_security_status(session.solarsystemid2, security_status)
    313         if security_level == SAFE_SEC:
    314             Container(parent=textCont, name='safeSecCont', align=uiconst.TOTOP, height=1, bgColor=TextColor.DISABLED)
    315             TextBody(parent=textCont, align=uiconst.TOTOP, state=uiconst.UI_NORMAL, pickState=PickState.ON, text=localization.GetByLabel('UI/SecurityStatus/StarterSecSafetyMessage', color=Color.RGBtoHex(*get_security_status_color(security_status)), link=localization.GetByLabel('UI/SecurityStatus/ShowOnMap', link=get_map_link(localization.GetByLabel('UI/SecurityStatus/LinkText'), None, STARMODE_SECURITY, get_range_id_by_security_status(session.solarsystemid2, security_status)))))
    316             Container(parent=textCont, name='safeSecCont', align=uiconst.TOTOP, height=1, bgColor=TextColor.DISABLED)
    317 
    318     def construct_buttons(self, currentSafetyLevel):
    319         self.securityButtons = []
    320         for safetyLevel in (const.shipSafetyLevelNone, const.shipSafetyLevelPartial, const.shipSafetyLevelFull):
    321             cont = ContainerAutoSize(parent=self, name='SecButtonCont', align=uiconst.TOTOP, alignMode=Align.CENTERTOP, padBottom=16)
    322             securityButton = SecurityButton(parent=cont, safetyLevel=safetyLevel, align=uiconst.CENTERTOP, width=160 * fontconst.fontSizeFactor)
    323             securityButton.SetSelected(safetyLevel == currentSafetyLevel)
    324             securityButton.OnClick = (self.OnSecurityButtonClick, securityButton)
    325             self.securityButtons.append(securityButton)
    326 
    327         if sm.GetService('crimewatchSvc').IsSafetyLockedToFullLevel():
    328             for button in self.securityButtons:
    329                 if button.safetyLevel == const.shipSafetyLevelFull:
    330                     button.text.SetText(GetByLabel('UI/Crimewatch/SafetyLevel/SafetyButtonLocked'))
    331 
    332     def DrawPointer(self, alignToTop):
    333         pointerWidth = DOCK_POINTER_LENGTH * 2
    334         pointerSideWidth = int(DOCK_POINTER_LENGTH * 2 / sqrt(2))
    335         pointerSideWidthHalf = pointerSideWidth / 2
    336         if alignToTop:
    337             clipperAlign = uiconst.CENTERTOP
    338             transformAlign = uiconst.CENTERBOTTOM
    339         else:
    340             clipperAlign = uiconst.CENTERBOTTOM
    341             transformAlign = uiconst.CENTERTOP
    342         clipperCont = Container(name='clipper', parent=self, width=pointerWidth, height=DOCK_POINTER_LENGTH, clipChildren=True, align=clipperAlign, top=-DOCK_POINTER_LENGTH)
    343         transform = Transform(name='transform', parent=clipperCont, align=transformAlign, rotation=pi / 4, width=pointerSideWidth, height=pointerSideWidth, top=-pointerSideWidthHalf)
    344         FillThemeColored(bgParent=transform, opacity=OPACITY_BG)
    345 
    346     def SetPosition(self, alignToTop):
    347         left, top, width, height = self.anchor.GetAbsolute()
    348         if alignToTop:
    349             self.top = top + (self.anchor.height + DOCK_POINTER_LENGTH + DOCK_MARGIN)
    350         else:
    351             self.top = top - (self.height + DOCK_POINTER_LENGTH + DOCK_MARGIN)
    352         self.left = left + (self.anchor.width - self.width) * 0.5
    353 
    354     def DrawFrame(self, alignToTop):
    355         if alignToTop:
    356             pointList = ((0, 0),
    357              ((self.width - DOCK_POINTER_LENGTH * 2) * 0.5, 0),
    358              (self.width * 0.5, -DOCK_POINTER_LENGTH),
    359              ((self.width + DOCK_POINTER_LENGTH * 2) * 0.5, 0),
    360              (self.width, 0),
    361              (self.width, self.height),
    362              (0, self.height))
    363         else:
    364             pointList = ((0, 0),
    365              (self.width, 0),
    366              (self.width, self.height),
    367              ((self.width + DOCK_POINTER_LENGTH * 2) * 0.5, self.height),
    368              (self.width * 0.5, self.height + DOCK_POINTER_LENGTH),
    369              ((self.width - DOCK_POINTER_LENGTH * 2) * 0.5, self.height),
    370              (0, self.height))
    371         if self.lineTrace is not None:
    372             self.lineTrace.Close()
    373         self.lineTrace = VectorLineTrace(name='Frame', parent=self, lineWidth=1.0, spriteEffect=trinity.TR2_SFX_FILL)
    374         self.lineTrace.isLoop = True
    375         for point in pointList:
    376             x, y = point
    377             color = sm.GetService('uiColor').GetUIColor(uiconst.COLORTYPE_UIHILIGHT)
    378             color = color[:3] + (0.25,)
    379             self.lineTrace.AddPoint((x, y), color)
    380 
    381     def OnKillFocus(self):
    382         if not self.confirmationButton or self.confirmationButton.destroyed:
    383             uthread.new(self.Close)
    384 
    385     def CloseSelector(self):
    386         uicore.animations.FadeOut(self, sleep=True)
    387         self.Close()
    388 
    389     def Close(self):
    390         super(SafetyLevelSelector, self).Close()
    391         self.on_closed()
    392 
    393     def OnSecurityButtonClick(self, securityButton):
    394         if securityButton.IsLocked():
    395             return
    396         if self.confirmationButton and not self.confirmationButton.destroyed:
    397             return
    398         PlaySound(uiconst.SOUND_BUTTON_CLICK)
    399         currentSafetyLevel = sm.GetService('crimewatchSvc').GetSafetyLevel()
    400         for button in self.securityButtons:
    401             if button.safetyLevel == securityButton.safetyLevel:
    402                 button.Blink()
    403                 if securityButton.safetyLevel < currentSafetyLevel:
    404                     self.confirmationButton = SafetyConfirmButton(parent=button.parent, left=8, color=shipSafetyConst.COLOR[securityButton.safetyLevel].GetRGBA(), idx=0, align=uiconst.CENTERRIGHT, safetyButton=button)
    405                     button.Highlight(False)
    406                     button.Pin()
    407                     uicore.registry.SetFocus(self.confirmationButton)
    408                 else:
    409                     sm.GetService('crimewatchSvc').SetSafetyLevel(securityButton.safetyLevel)
    410 
    411     def SetSafetyLevel(self, safetyLevel):
    412         selected = None
    413         for button in self.securityButtons:
    414             if button.safetyLevel == safetyLevel:
    415                 selected = button
    416             else:
    417                 button.SetSelected(False)
    418 
    419         selected.SetSelected(True, sleep=True)
    420 
    421 
    422 class SecurityButton(Container):
    423     default_height = 36
    424     default_align = uiconst.TOPLEFT
    425     default_state = uiconst.UI_NORMAL
    426     default_hintDelay = HINT_DELAY
    427     lineWidth = 5.0
    428     lineSeperation = 14.0
    429 
    430     def ApplyAttributes(self, attributes):
    431         super(SecurityButton, self).ApplyAttributes(attributes)
    432         self.pinned = False
    433         self.isSelected = False
    434         self.safetyLevel = attributes.get('safetyLevel')
    435         cont = Container(name='content', parent=self, padding=3)
    436         color = shipSafetyConst.COLOR[self.safetyLevel]
    437         button_text = GetByLabel(shipSafetyConst.ACTION_LABEL[self.safetyLevel])
    438         self.text = eveLabel.EveHeaderSmall(name='buttonLabel', parent=cont, color=color.GetRGBA(), text=button_text, align=uiconst.CENTER, opacity=DESELECTED_BUTTON_OPACITY)
    439         self.defaultSprite = Sprite(parent=self, name='default', texturePath='res:/UI/Texture/Crimewatch/SafetyButton_Default.png', align=uiconst.TOALL, state=uiconst.UI_DISABLED, color=color.GetRGBA(), opacity=DESELECTED_BUTTON_OPACITY)
    440         self.highlightSprite = Sprite(parent=self, name='highlight', texturePath='res:/UI/Texture/Crimewatch/SafetyButton_Highlight.png', align=uiconst.TOALL, state=uiconst.UI_DISABLED, color=color.GetRGBA())
    441         self.highlightSprite.Hide()
    442         self.leftArrowSprite = Sprite(parent=self, name='leftArrows', texturePath='res:/UI/Texture/Crimewatch/Safety_Selection.png', align=uiconst.CENTERLEFT, pos=(-24, 0, 16, 13), state=uiconst.UI_DISABLED, color=color.GetRGBA(), opacity=0.0)
    443         self.rightArrows = Transform(parent=self, name='rightArrowsTranform', align=uiconst.CENTERRIGHT, pos=(-24, 0, 16, 13), state=uiconst.UI_DISABLED, rotation=pi)
    444         self.rightArrowSprite = Sprite(parent=self.rightArrows, name='rightArrows', texturePath='res:/UI/Texture/Crimewatch/Safety_Selection.png', align=uiconst.CENTERRIGHT, pos=(0, 0, 16, 13), state=uiconst.UI_DISABLED, color=color.GetRGBA(), opacity=0.0)
    445 
    446     def IsLocked(self):
    447         if sm.GetService('crimewatchSvc').IsSafetyLockedToFullLevel():
    448             return self.safetyLevel != const.shipSafetyLevelFull
    449         return self.IsAlphaLockedAndRed()
    450 
    451     def IsAlphaLockedAndRed(self):
    452         if sm.GetService('crimewatchSvc').IsSafetyAlphaLocked():
    453             return self.safetyLevel == const.shipSafetyLevelNone
    454         return False
    455 
    456     def GetHint(self):
    457         color = shipSafetyConst.COLOR[self.safetyLevel]
    458         parts = []
    459         if self.IsAlphaLockedAndRed():
    460             parts.append(GetByLabel('UI/Crimewatch/SafetyLevel/SafetyButtonNoneOptionHintAlphaLocked'))
    461         button_hint = shipSafetyConst.STATE_HINT[self.safetyLevel]
    462         parts.append(GetByLabel(button_hint, color=color.GetHex()))
    463         return '\n\n'.join(parts)
    464 
    465     def GetTooltipPointer(self):
    466         return uiconst.POINT_RIGHT_2
    467 
    468     def Pin(self):
    469         self.pinned = True
    470         self.text.opacity = 1.0
    471         self.defaultSprite.opacity = 1.0
    472 
    473     def Unpin(self):
    474         self.pinned = False
    475         self.text.opacity = DESELECTED_BUTTON_OPACITY
    476         self.defaultSprite.opacity = DESELECTED_BUTTON_OPACITY
    477 
    478     def SetSelected(self, selected, sleep = False):
    479         self.isSelected = selected
    480         if selected:
    481             self.text.opacity = 1.0
    482             self.defaultSprite.opacity = 1.0
    483             self.leftArrowSprite.opacity = 1.0
    484             self.rightArrowSprite.opacity = 1.0
    485             curveSet = uicore.animations.MoveInFromLeft(self.leftArrowSprite, loops=1, duration=0.4, amount=20)
    486             uicore.animations.MoveInFromRight(self.rightArrowSprite, loops=1, duration=0.4, amount=20, curveSet=curveSet, sleep=sleep)
    487         else:
    488             self.text.opacity = DESELECTED_BUTTON_OPACITY
    489             self.defaultSprite.opacity = DESELECTED_BUTTON_OPACITY
    490             if self.leftArrowSprite.opacity > 0.0 or self.rightArrowSprite.opacity > 0.0:
    491                 curveSet = uicore.animations.FadeOut(self.leftArrowSprite, duration=0.25)
    492                 uicore.animations.FadeOut(self.rightArrowSprite, duration=0.25, curveSet=curveSet)
    493 
    494     def Highlight(self, enable):
    495         if self.IsLocked():
    496             return
    497         if enable and not self.IsConfirmButtonActive():
    498             self.defaultSprite.Hide()
    499             self.highlightSprite.Show()
    500             self.text.opacity = 1.0
    501         else:
    502             self.defaultSprite.Show()
    503             self.highlightSprite.Hide()
    504             if self.isSelected or self.pinned:
    505                 self.text.opacity = 1.0
    506             else:
    507                 self.text.opacity = DESELECTED_BUTTON_OPACITY
    508 
    509     def OnMouseEnter(self):
    510         if not self.IsLocked():
    511             PlaySound(uiconst.SOUND_BUTTON_HOVER)
    512             self.Highlight(True)
    513 
    514     def OnMouseExit(self):
    515         if not self.IsLocked():
    516             self.Highlight(False)
    517 
    518     def IsConfirmButtonActive(self):
    519         return not (self.parent.parent.confirmationButton is None or self.parent.parent.confirmationButton.destroyed)
    520 
    521     def Blink(self, sleep = True):
    522         color = shipSafetyConst.COLOR[self.safetyLevel]
    523         curveSet = uicore.animations.SpGlowFadeTo(self.highlightSprite, startColor=color.GetRGBA(), glowExpand=0.0)
    524         uicore.animations.SpGlowFadeTo(self.defaultSprite, startColor=color.GetRGBA(), glowExpand=0.0, curveSet=curveSet)
    525 
    526 
    527 class SafetyConfirmButton(Container):
    528     default_width = 50
    529     default_height = 32
    530     default_state = uiconst.UI_NORMAL
    531     default_align = uiconst.TOPRIGHT
    532     default_name = 'SafetyConfirmButton'
    533     default_hintDelay = HINT_DELAY
    534 
    535     def ApplyAttributes(self, attributes):
    536         super(SafetyConfirmButton, self).ApplyAttributes(attributes)
    537         self.isTabStop = True
    538         self.canBeModified = attributes.get('canBeModified', True)
    539         self.safetyButton = attributes.get('safetyButton')
    540         self.color = attributes.get('color', (1, 1, 1, 1))
    541         self.bgColor = (self.color[0] * 0.3,
    542          self.color[1] * 0.3,
    543          self.color[2] * 0.3,
    544          1.0)
    545         self.label = eveLabel.EveLabelSmall(parent=self, text=localization.GetByLabel('UI/Common/Confirm'), color=self.color, align=uiconst.CENTER)
    546         self.frame = Frame(parent=self, frameConst=('ui_105_32_1', 8, -4), color=self.color, opacity=0.5)
    547         self.fill = Fill(parent=self, color=self.bgColor)
    548         color = shipSafetyConst.COLOR[self.safetyButton.safetyLevel]
    549         self.SetHint(localization.GetByLabel('UI/Crimewatch/SafetyLevel/SafetyConfirmHint', color=color.GetHex()))
    550         width, _ = self.label.GetAbsoluteSize()
    551         self.width = max(self.width, width + 4)
    552 
    553     def OnMouseEnter(self):
    554         if self.canBeModified:
    555             PlaySound(uiconst.SOUND_BUTTON_HOVER)
    556             self.label.SetTextColor(Color.WHITE)
    557 
    558     def OnMouseExit(self):
    559         if self.canBeModified:
    560             self.label.SetTextColor(self.color)
    561 
    562     def OnClick(self):
    563         if not self.canBeModified:
    564             return
    565         PlaySound(uiconst.SOUND_BUTTON_CLICK)
    566         self.state = uiconst.UI_DISABLED
    567         self.safetyButton.Blink()
    568         self.Blink()
    569         sm.GetService('crimewatchSvc').SetSafetyLevel(self.safetyButton.safetyLevel)
    570         uthread.new(self._CloseButton)
    571 
    572     def OnKillFocus(self):
    573         uthread.new(self._CloseButton)
    574 
    575     def _CloseButton(self):
    576         if not self or self.destroyed:
    577             return
    578         self.safetyButton.Unpin()
    579         uicore.registry.SetFocus(self.parent.parent)
    580         uicore.animations.FadeOut(self, duration=0.1, sleep=True)
    581         self.Close()
    582 
    583     def Blink(self):
    584         curveSet = uicore.animations.SpGlowFadeTo(self.frame, startColor=self.color, glowExpand=0.0)
    585         uicore.animations.SpGlowFadeTo(self.fill, glowExpand=0.0, startColor=self.bgColor, curveSet=curveSet, sleep=True)
    586 
    587     def GetTooltipPointer(self):
    588         return uiconst.POINT_LEFT_2