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

tab.py (21667B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\tab.py
      2 import math
      3 import telemetry
      4 import trinity
      5 import uthread
      6 import uthread2
      7 from carbon.client.script.environment.AudioUtil import PlaySound
      8 from carbonui import uiconst, fontconst
      9 from carbonui.primitives.container import Container
     10 from carbonui.primitives.dragdrop import DragDropObject
     11 from carbonui.primitives.fill import Fill
     12 from carbonui.text.color import TextColor
     13 from carbonui.uianimations import animations
     14 from carbonui.uicore import uicore
     15 from carbonui.util.various_unsorted import GetWindowAbove
     16 from eve.client.script.ui import eveThemeColor
     17 from eve.client.script.ui.control import eveLabel
     18 from eve.client.script.ui.control.eveIcon import Icon
     19 from eveexceptions import UserError, EatsExceptions
     20 from logmodule import LogException
     21 from carbonui.loggers.message_bus.tabMessenger import TabMessenger
     22 OPACITY_DEFAULT = 1.0
     23 OPACITY_DISABLED = 0.35
     24 OPACITY_SELECTED = 1.0
     25 
     26 class Tab(Container):
     27     default_enabled = True
     28     default_fontsize = fontconst.EVE_MEDIUM_FONTSIZE
     29     default_fontStyle = None
     30     default_fontFamily = None
     31     default_fontPath = None
     32     default_labelPadding = 0
     33     selectedBG = None
     34 
     35     @telemetry.ZONE_METHOD
     36     def ApplyAttributes(self, attributes):
     37         Container.ApplyAttributes(self, attributes)
     38         self.enabled = attributes.get('enabled', self.default_enabled)
     39         self.fontStyle = attributes.get('fontStyle', self.default_fontStyle)
     40         self.fontFamily = attributes.get('fontFamily', self.default_fontFamily)
     41         self.fontPath = attributes.get('fontPath', self.default_fontPath)
     42         self.fontsize = attributes.get('fontsize', self.default_fontsize)
     43         self.labelPadding = attributes.get('labelPadding', self.default_labelPadding)
     44         self.tabID = None
     45         self.selecting = 0
     46         self.icon = None
     47         self.headerIconCont = None
     48         self.sr.LoadTabCallback = None
     49         self.isTabStop = True
     50         self._activity_indicator = None
     51         self._hovered = False
     52         self._gap_left = 0
     53         self._gap_right = 0
     54         self._detachallowed = False
     55         self.ConstructLayout()
     56         self.isTabStop = False
     57 
     58     @property
     59     def gap_left(self):
     60         return self._gap_left
     61 
     62     @gap_left.setter
     63     def gap_left(self, value):
     64         if self._gap_left != value:
     65             self._gap_left = value
     66             self._update_activity_indicator_padding()
     67 
     68     @property
     69     def gap_right(self):
     70         return self._gap_right
     71 
     72     @gap_right.setter
     73     def gap_right(self, value):
     74         if self._gap_right != value:
     75             self._gap_right = value
     76             self._update_activity_indicator_padding()
     77 
     78     def _update_activity_indicator_padding(self):
     79         self._activity_indicator.padding = self._get_activity_indicator_padding()
     80 
     81     def _get_activity_indicator_padding(self):
     82         pad_left = int(math.floor(max(self._gap_left - 1, 0) / 2.0))
     83         pad_right = int(math.ceil(max(self._gap_right - 1, 0) / 2.0))
     84         return (-pad_left,
     85          0,
     86          -pad_right,
     87          0)
     88 
     89     def GetHeight(self):
     90         if self.icon:
     91             return self.icon.height
     92         return self.label.textheight
     93 
     94     @telemetry.ZONE_METHOD
     95     def ConstructLayout(self):
     96         self._ConstructClipper()
     97         self._ConstructLabel()
     98         self._ConstructActivityIndicator()
     99         self._ConstructUnderlay()
    100 
    101     def _ConstructActivityIndicator(self):
    102         self._activity_indicator = ActivityIndicator(parent=self, align=uiconst.TOALL, padding=self._get_activity_indicator_padding())
    103 
    104     def _ConstructClipper(self):
    105         self.clipper = Container(name='labelClipper', parent=self, align=uiconst.TOALL, clipChildren=True, state=uiconst.UI_PICKCHILDREN)
    106 
    107     def _ConstructLabel(self):
    108         self.label = eveLabel.EveLabelMedium(parent=self.clipper, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, autoFadeSides=8, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.0, offer_auxiliary_copy_option=False)
    109 
    110     def _ConstructUnderlay(self):
    111         pass
    112 
    113     @telemetry.ZONE_METHOD
    114     def Startup(self, tabgroup, data):
    115         from carbonui.primitives.stretchspritehorizontal import StretchSpriteHorizontal
    116         self.blinkDrop = StretchSpriteHorizontal(parent=self, align=uiconst.TOTOP_NOPUSH, color=(0.5, 0.5, 0.5, 0.0), name='blinkDrop', height=11, leftEdgeSize=6, rightEdgeSize=6, offset=-2, blendMode=trinity.TR2_SBM_ADD, texturePath='res:/UI/Texture/Lines/BLUR4.png')
    117         self.sr.args = data.get('args', None)
    118         self.tabID = self.sr.args
    119         self.sr.grab = [0, 0]
    120         self.tabgroup = tabgroup
    121         self._selected = False
    122         self.sr.panel = data.get('panel', None)
    123         self.sr.panelparent = data.get('panelparent', None)
    124         self.sr.code = data.get('code', None)
    125         self.sr.LoadTabCallback = data.get('LoadTabCallback', None)
    126         self.SetLabel(data.get('label', None))
    127         self.SetIcon(data.get('icon', None))
    128         self.Deselect(False)
    129         self.hint = data.get('hint', None)
    130         if hasattr(self.sr.code, 'GetTabMenu'):
    131             self.GetMenu = lambda : self.sr.code.GetTabMenu(self)
    132         if hasattr(self.sr.panel, 'sr'):
    133             self.sr.panel.sr.tab = self
    134         self.name = data.name or data.label
    135         if not self.enabled:
    136             self.Disable()
    137         self.callback = data.callback
    138 
    139     def GetPanel(self):
    140         try:
    141             return self.sr.panel
    142         except AttributeError:
    143             return None
    144 
    145     def Confirm(self, *args):
    146         self.OnClick()
    147 
    148     def OnSetFocus(self, *args):
    149         pass
    150 
    151     def OnKillFocus(self, *etc):
    152         pass
    153 
    154     def SetLabel(self, label, hint = None):
    155         if self.destroyed:
    156             return
    157         self.label.text = label
    158         self.UpdateTabSize()
    159         self.label.hint = hint
    160         self.tabgroup.UpdateSizes()
    161 
    162     def UpdateTabSize(self):
    163         self.sr.width = self.label.left + self.label.width
    164 
    165     def OnDropData(self, dragObj, nodes):
    166         if not self.enabled:
    167             return
    168         if hasattr(self, 'OnTabDropData'):
    169             if self.OnTabDropData(dragObj, nodes):
    170                 self.BlinkOnDrop()
    171         elif isinstance(self.sr.panel, DragDropObject) and hasattr(self.sr.panel, 'OnDropData'):
    172             if self.sr.panel.OnDropData(dragObj, nodes):
    173                 self.BlinkOnDrop()
    174 
    175     def Blink(self, onoff = 1):
    176         active = bool(onoff)
    177         if active and not self.IsSelected():
    178             self._activity_indicator.mark_new(duration=5.0)
    179             animations.MorphScalar(self.label, 'glowBrightness', startVal=0.0, endVal=1.0, curveType=uiconst.ANIM_WAVE, loops=3, duration=1.0)
    180         else:
    181             self._activity_indicator.activity_level = ActivityLevel.IDLE
    182         self._update_label_color()
    183 
    184     def BlinkOnDrop(self):
    185         uicore.animations.FadeTo(self.blinkDrop, 0.0, 1.0, duration=0.25, curveType=uiconst.ANIM_WAVE, loops=2)
    186 
    187     def SetUtilMenu(self, utilMenuFunc):
    188         from eve.client.script.ui.control.utilMenu import UtilMenu
    189         self.label.left = 14
    190         utilMenuIcon = UtilMenu(menuAlign=uiconst.TOPLEFT, parent=self, align=uiconst.TOPLEFT, GetUtilMenu=utilMenuFunc, texturePath='res:/UI/Texture/Icons/73_16_50.png', pos=(uiconst.defaultPadding,
    191          uiconst.defaultPadding,
    192          14,
    193          14))
    194         self.UpdateTabSize()
    195         self.tabgroup.UpdateSizes()
    196         return utilMenuIcon
    197 
    198     def SetIcon(self, iconNo, shiftLabel = 14, hint = None, menufunc = None):
    199         if self.icon:
    200             self.icon.Close()
    201         if self.headerIconCont:
    202             self.headerIconCont.Close()
    203             self.headerIconCont = None
    204         self.sr.hint = hint
    205         if iconNo is None:
    206             if self.label:
    207                 self.label.left = 0
    208         else:
    209             self.headerIconCont = Container(name='headerIconCont', parent=self, pos=(2, 3, 16, 16), align=uiconst.RELATIVE, ignoreSize=True)
    210             self.icon = Icon(icon=iconNo, parent=self.headerIconCont, pos=(0, 0, 16, 16), align=uiconst.TOPLEFT, idx=0, state=uiconst.UI_DISABLED)
    211             if self.label:
    212                 self.label.left = shiftLabel
    213             if menufunc:
    214                 self.icon.GetMenu = menufunc
    215                 self.icon.expandOnLeft = 1
    216                 self.icon.state = uiconst.UI_NORMAL
    217         self.sr.hint = hint
    218         self.UpdateTabSize()
    219         self.tabgroup.UpdateSizes()
    220 
    221     def OnClick(self, *args):
    222         if not self.enabled:
    223             return
    224         if self.selecting:
    225             return
    226         self.tabgroup.state = uiconst.UI_DISABLED
    227         PlaySound(uiconst.SOUND_BUTTON_CLICK)
    228         try:
    229             self.Select()
    230             uicore.registry.SetFocus(self)
    231         finally:
    232             self.tabgroup.state = uiconst.UI_PICKCHILDREN
    233 
    234     def IsSelected(self):
    235         return self._selected
    236 
    237     @telemetry.ZONE_METHOD
    238     def Deselect(self, notify = True):
    239         wasSelected = self._selected
    240         self._selected = False
    241         self.ShowDeselected_()
    242         if self.sr.panel:
    243             self.sr.panel.display = False
    244             if notify:
    245                 if wasSelected and hasattr(self.sr.panel, 'OnSelectedTabDeselected'):
    246                     self.sr.panel.OnSelectedTabDeselected()
    247                 if hasattr(self.sr.panel, 'OnTabDeselect'):
    248                     self.sr.panel.OnTabDeselect()
    249                 elif hasattr(self.sr.panel, 'UnloadPanel'):
    250                     self.sr.panel.UnloadPanel()
    251         if self.sr.panelparent:
    252             self.sr.panelparent.display = False
    253             if notify:
    254                 if wasSelected and hasattr(self.sr.panelparent, 'OnSelectedTabDeselected'):
    255                     self.sr.panelparent.OnSelectedTabDeselected()
    256                 if hasattr(self.sr.panelparent, 'OnTabDeselect'):
    257                     self.sr.panelparent.OnTabDeselect()
    258                 elif hasattr(self.sr.panelparent, 'UnloadPanel'):
    259                     self.sr.panelparent.UnloadPanel()
    260 
    261     @telemetry.ZONE_METHOD
    262     def ShowDeselected_(self):
    263         if self.selectedBG:
    264             self.selectedBG.Deselect()
    265         self._update_label_color()
    266 
    267     @telemetry.ZONE_METHOD
    268     def ShowSelected_(self):
    269         if self.selectedBG:
    270             self.selectedBG.Select()
    271         self._update_label_color()
    272 
    273     @telemetry.ZONE_METHOD
    274     def Select(self, silently = 0, useCallback = True, saveSelectedName = True):
    275         if self.destroyed or self.selecting:
    276             return
    277         self.selecting = 1
    278         self.Blink(0)
    279         oldTabID = self.tabgroup.GetSelectedID()
    280         if self is None or self.destroyed:
    281             self.selecting = 0
    282             self.tabgroup.state = uiconst.UI_PICKCHILDREN
    283             return
    284         if len(self.tabgroup.linkedrows):
    285             for tabgroup in self.tabgroup.linkedrows:
    286                 if self in tabgroup.mytabs:
    287                     continue
    288                 tabgroup.SetOrder(0)
    289 
    290         for each in self.tabgroup.sr.tabs:
    291             if each.IsSelected():
    292                 if hasattr(self.sr.code, 'UnloadTabPanel'):
    293                     self.sr.code.UnloadTabPanel(each.sr.args, each.sr.panel, each.tabgroup)
    294             if each == self:
    295                 continue
    296             notify = True
    297             if each.sr.panel and each.sr.panel is self.sr.panel or each.sr.panelparent and each.sr.panelparent is self.sr.panelparent:
    298                 notify = False
    299             each.Deselect(notify)
    300 
    301         self._selected = True
    302         self.tabgroup._set_selected_tab(self)
    303         self.ShowSelected_()
    304         if self.sr.panelparent:
    305             self.sr.panelparent.display = True
    306             if hasattr(self.sr.panelparent, 'OnTabSelect'):
    307                 self.sr.panelparent.OnTabSelect()
    308             elif hasattr(self.sr.panelparent, 'LoadPanel'):
    309                 self.sr.panelparent.LoadPanel()
    310         if self.sr.panel:
    311             self.sr.panel.display = True
    312             if hasattr(self.sr.panel, 'OnTabSelect'):
    313                 self.sr.panel.OnTabSelect()
    314             elif hasattr(self.sr.panel, 'LoadPanel'):
    315                 self.sr.panel.LoadPanel()
    316         if useCallback and self.tabgroup.callback:
    317             self.tabgroup.callback(self.tabgroup.GetSelectedID(), oldTabID)
    318         err = None
    319         if self.sr.LoadTabCallback:
    320             try:
    321                 self.sr.LoadTabCallback(self.sr.args, self.sr.panel, self.tabgroup)
    322             finally:
    323                 self.selecting = 0
    324 
    325         elif hasattr(self.sr.code, 'LoadTabPanel'):
    326             try:
    327                 self.sr.code.LoadTabPanel(self.sr.args, self.sr.panel, self.tabgroup)
    328             finally:
    329                 self.selecting = 0
    330 
    331         elif getattr(self.sr.code, 'Load', None):
    332             try:
    333                 self.sr.code.Load(self.sr.args)
    334             except Exception as err:
    335                 LogException()
    336                 if self.destroyed:
    337                     return
    338                 wnd = GetWindowAbove(self)
    339                 if wnd and not wnd.destroyed:
    340                     wnd.HideLoad()
    341 
    342         if not silently:
    343             par = self.sr.panelparent or self.sr.panel
    344             wnd = GetWindowAbove(self)
    345             if par and wnd and wnd == uicore.registry.GetActive():
    346                 uthread.new(uicore.registry.SetFocus, par)
    347         if self.destroyed:
    348             return
    349         self.UpdateSelectedSettings(saveSelectedName)
    350         if self and not self.destroyed:
    351             self.tabgroup.UpdateSizes()
    352             self.selecting = 0
    353         if err and isinstance(err, UserError):
    354             raise err
    355         self._LogProtoEvent()
    356 
    357     def UpdateSelectedSettings(self, saveSelectedName):
    358         if self.tabgroup.settingsID:
    359             self.SaveSelectedTabIndex()
    360             if saveSelectedName:
    361                 self.SaveSelectedTabName()
    362 
    363     def SaveSelectedTabIndex(self):
    364         settings.user.tabgroups.Set(self.tabgroup.settingsID, self.tabgroup.sr.tabs.index(self))
    365 
    366     def SaveSelectedTabName(self):
    367         settings.user.tabgroups.Set('%s_names' % self.tabgroup.settingsID, self.label.text if self.label else None)
    368 
    369     def OnMouseDown(self, *args):
    370         if not self.enabled:
    371             return
    372         self._detachallowed = 1
    373         aL, aT, aW, aH = self.GetAbsolute()
    374         self.sr.grab = [uicore.uilib.x - aL, uicore.uilib.y - aT]
    375         if self.selectedBG:
    376             self.selectedBG.OnMouseDown()
    377 
    378     def OnMouseUp(self, *args):
    379         if not self.enabled:
    380             return
    381         self._detachallowed = 0
    382         if self.selectedBG:
    383             self.selectedBG.OnMouseUp()
    384 
    385     def OnMouseMove(self, *args):
    386         if not self.enabled:
    387             return
    388         if self._detachallowed and uicore.uilib.mouseTravel > 24 and hasattr(self.sr.code, 'Detach'):
    389             uthread.new(self.DoDetach)
    390 
    391     def OnMouseEnter(self, *args):
    392         if not self.enabled:
    393             return
    394         self._hovered = True
    395         PlaySound(uiconst.SOUND_BUTTON_HOVER)
    396         if self._selected:
    397             return
    398         if self.selectedBG:
    399             self.selectedBG.OnMouseEnter()
    400         self._update_label_color()
    401         self._activity_indicator.mark_seen()
    402 
    403     def OnMouseExit(self, *args):
    404         if not self.enabled:
    405             return
    406         self._hovered = False
    407         if self.selectedBG:
    408             self.selectedBG.OnMouseExit()
    409         self._update_label_color()
    410 
    411     def DoDetach(self, *args):
    412         if self is not None and not self.destroyed:
    413             if self.sr.code.Detach(self.sr.panel, self.sr.grab):
    414                 if self is not None and not self.destroyed:
    415                     self.Close()
    416             else:
    417                 self._detachallowed = 0
    418 
    419     def GetHint(self):
    420         if self.hint:
    421             return self.hint
    422 
    423     @EatsExceptions('protoClientLogs')
    424     def _LogProtoEvent(self):
    425         if self.tabgroup.analyticID:
    426             message_bus = TabMessenger(sm.GetService('publicGatewaySvc'))
    427             message_bus.tab_selected(self.analyticContext, self._GetAnalyticsTabID(), self.tabgroup.analyticID)
    428 
    429     def _GetAnalyticsTabID(self):
    430         tab_id = None
    431         selected_id = self.tabgroup.GetSelectedID()
    432         if isinstance(selected_id, tuple):
    433             tab_id = selected_id[0]
    434         elif isinstance(selected_id, str):
    435             tab_id = selected_id
    436         if not tab_id:
    437             tab_id = self.tabgroup.GetSelectedIdx()
    438         return str(tab_id)
    439 
    440     def Enable(self, *args):
    441         super(Tab, self).Enable(*args)
    442         self.enabled = True
    443         self.state = uiconst.UI_NORMAL
    444         self.opacity = OPACITY_DEFAULT
    445 
    446     def Disable(self, *args):
    447         super(Tab, self).Disable(*args)
    448         self.enabled = False
    449         self.state = uiconst.UI_DISABLED
    450         self.opacity = OPACITY_DISABLED
    451 
    452     def _update_label_color(self):
    453         if self._hovered:
    454             duration = 0.1
    455         else:
    456             duration = 0.3
    457         animations.SpColorMorphTo(self.label, endColor=self._get_label_color(), duration=duration)
    458 
    459     def _get_label_color(self):
    460         if self._selected or self._hovered:
    461             return TextColor.HIGHLIGHT
    462         elif self._activity_indicator.activity_level == ActivityLevel.NEW:
    463             return TextColor.NORMAL
    464         else:
    465             return TextColor.SECONDARY
    466 
    467 
    468 class ActivityLevel(object):
    469     IDLE = 1
    470     UNSEEN = 2
    471     NEW = 3
    472 
    473 
    474 class ActivityIndicator(Container):
    475     UNDERLAY_OPACITY_ACTIVE = 0.3
    476     INDICATOR_OPACITY_ACTIVE = 1.0
    477     INDICATOR_WIDTH_NEW = 16
    478     INDICATOR_WIDTH = 8
    479 
    480     def __init__(self, parent = None, align = uiconst.TOALL, activity_level = ActivityLevel.IDLE, padding = 0):
    481         self._activity_level = activity_level
    482         self._underlay = None
    483         self._indicator = None
    484         self._mark_after_thread = None
    485         super(ActivityIndicator, self).__init__(parent=parent, align=align, padding=padding)
    486         self._update()
    487 
    488     @property
    489     def activity_level(self):
    490         return self._activity_level
    491 
    492     @activity_level.setter
    493     def activity_level(self, value):
    494         if self._activity_level != value:
    495             previous_activity_level = self._activity_level
    496             self._activity_level = value
    497             self._update(previous_activity_level=previous_activity_level)
    498         if self._mark_after_thread:
    499             self._mark_after_thread.kill()
    500             self._mark_after_thread = None
    501 
    502     def mark_seen(self):
    503         if self._activity_level == ActivityLevel.NEW:
    504             self.activity_level = ActivityLevel.UNSEEN
    505 
    506     def mark_new(self, duration = None):
    507         self.activity_level = ActivityLevel.NEW
    508         if duration is not None:
    509             self._mark_after_thread = uthread2.StartTasklet(self._mark_after, ActivityLevel.UNSEEN, duration)
    510 
    511     def _mark_after(self, activityLevel, duration):
    512         uthread2.sleep(duration)
    513         self.activity_level = activityLevel
    514 
    515     def _update(self, previous_activity_level = None):
    516         if self._activity_level in (ActivityLevel.IDLE, ActivityLevel.UNSEEN):
    517             if self._underlay is not None:
    518                 if previous_activity_level is not None:
    519                     animations.FadeOut(self._underlay, duration=0.6)
    520                 else:
    521                     self._underlay.opacity = 0.0
    522         elif self._activity_level == ActivityLevel.NEW:
    523             if self._underlay is None:
    524                 self._create_underlay()
    525             if previous_activity_level is not None:
    526                 animations.FadeIn(self._underlay, endVal=self.UNDERLAY_OPACITY_ACTIVE, duration=0.6)
    527             else:
    528                 self._underlay.opacity = self.UNDERLAY_OPACITY_ACTIVE
    529         if self._activity_level == ActivityLevel.IDLE:
    530             if self._indicator is not None:
    531                 if previous_activity_level is not None:
    532                     animations.FadeOut(self._indicator, duration=0.3)
    533                     animations.MorphScalar(self._indicator, 'width', startVal=self._indicator.width, endVal=0, duration=0.6)
    534                 else:
    535                     self._indicator.opacity = 0.0
    536                     self._indicator.width = 0
    537         elif self._activity_level in (ActivityLevel.UNSEEN, ActivityLevel.NEW):
    538             if self._indicator is None:
    539                 self._create_indicator()
    540             indicator_width = self._get_indicator_width()
    541             if previous_activity_level is not None:
    542                 animations.FadeIn(self._indicator, duration=0.3)
    543                 animations.MorphScalar(self._indicator, 'width', startVal=self._indicator.width, endVal=indicator_width, duration=0.6)
    544             else:
    545                 self._indicator.opacity = self.INDICATOR_OPACITY_ACTIVE
    546                 self._indicator.width = indicator_width
    547 
    548     def _create_underlay(self):
    549         if self._underlay is None:
    550             self._underlay = Fill(parent=self, align=uiconst.TOALL, color=eveThemeColor.THEME_ALERT, opacity=0.0)
    551 
    552     def _create_indicator(self):
    553         if self._indicator is None:
    554             self._indicator = Fill(parent=self, idx=0, align=uiconst.CENTERBOTTOM, width=self._get_indicator_width(), height=1, color=eveThemeColor.THEME_ALERT, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, opacity=0.0)
    555 
    556     def _get_indicator_width(self):
    557         if self.activity_level == ActivityLevel.NEW:
    558             return self.INDICATOR_WIDTH_NEW
    559         return self.INDICATOR_WIDTH
    560 
    561     def OnColorThemeChanged(self):
    562         if self._indicator is not None:
    563             self._indicator.color = tuple(eveThemeColor.THEME_ALERT[:3]) + (self._indicator.opacity,)
    564         if self._underlay is not None:
    565             self._underlay.color = tuple(eveThemeColor.THEME_ALERT[:3]) + (self._underlay.opacity,)