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

combo.py (27760B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\combo.py
      2 import logging
      3 import random
      4 import weakref
      5 import blue
      6 import eveformat
      7 import eveicon
      8 import localization
      9 import uthread
     10 from carbon.client.script.environment.AudioUtil import PlaySound
     11 from carbonui import fontconst, uiconst
     12 from carbonui.button import styling
     13 from carbonui.button.const import HEIGHT_NORMAL
     14 from carbonui.control.comboEntries import ComboCaptionEntry, ComboEntry, ComboSeparatorEntry
     15 from carbonui.control.comboEntryData import BaseComboEntryData, ComboEntryData, ComboEntryDataCaption, ComboEntryDataSeparator, ComboEntryDataText
     16 from carbonui.control.contextMenu.menuUtil import ClearMenuLayer
     17 from carbonui.control.scrollentries import ScrollEntryNode
     18 from carbonui.primitives.container import Container
     19 from carbonui.primitives.frame import Frame
     20 from carbonui.primitives.line import Line
     21 from carbonui.text.color import TextColor
     22 from carbonui.uianimations import animations
     23 from carbonui.uiconst import Density
     24 from carbonui.uicore import uicore
     25 from carbonui.util.color import Color
     26 from carbonui.util.various_unsorted import GetAttrs, GetBrowser
     27 from eve.client.script.ui import eveColor
     28 from eve.client.script.ui.control import eveLabel
     29 from carbonui.decorative.blurredSceneUnderlay import BlurredSceneUnderlay
     30 from carbonui.control.buttonIcon import ButtonIcon
     31 from eve.client.script.ui.control.entries.text import Text
     32 from eve.client.script.ui.control.eveIcon import Icon
     33 from eve.client.script.ui.control.eveScroll import Scroll
     34 from eve.client.script.ui.control.glowSprite import GlowSprite
     35 log = logging.getLogger(__name__)
     36 OPACITY_TEXT_IDLE = TextColor.NORMAL.opacity
     37 OPACITY_TEXT_HOVER = 1.0
     38 ALIGNMENTS_THAT_DONT_SUPPORT_AUTO_WIDTH = (uiconst.TOTOP,
     39  uiconst.TOBOTTOM,
     40  uiconst.TOALL,
     41  uiconst.TOLEFT_PROP,
     42  uiconst.TORIGHT_PROP)
     43 
     44 class Combo(Container):
     45     default_adjustWidth = True
     46     default_align = uiconst.TOPLEFT
     47     default_callback = None
     48     default_cursor = uiconst.UICURSOR_SELECT
     49     default_fontFamily = None
     50     default_fontPath = None
     51     default_fontStyle = None
     52     default_fontsize = fontconst.EVE_MEDIUM_FONTSIZE
     53     default_hasClearButton = False
     54     default_iconOnly = False
     55     default_label = ''
     56     default_labelleft = None
     57     default_maxVisibleEntries = 8
     58     default_name = 'combo'
     59     default_noChoiceLabel = None
     60     default_nothingSelectedText = ''
     61     default_options = []
     62     default_prefskey = None
     63     default_select = None
     64     default_state = uiconst.UI_NORMAL
     65     default_width = 140
     66     default_height = HEIGHT_NORMAL
     67     default_density = Density.NORMAL
     68     isTabStop = 1
     69     _hovered = False
     70 
     71     def ApplyAttributes(self, attributes):
     72         self._comboDropDown = None
     73         self._disabled = False
     74         self._expanding = False
     75         self.clearButton = None
     76         self.selectedValue = None
     77         self.sr.label = None
     78         self._entries = None
     79         self._hidden_values = []
     80         super(Combo, self).ApplyAttributes(attributes)
     81         self.adjustWidth = attributes.get('adjustWidth', self.default_adjustWidth)
     82         if self.align not in ALIGNMENTS_THAT_DONT_SUPPORT_AUTO_WIDTH and attributes.get('width', 0) > 0:
     83             self.adjustWidth = False
     84         self.prefskey = attributes.get('prefskey', self.default_prefskey)
     85         self.fontStyle = attributes.get('fontStyle', self.default_fontStyle)
     86         self.fontFamily = attributes.get('fontFamily', self.default_fontFamily)
     87         self.fontPath = attributes.get('fontPath', self.default_fontPath)
     88         self.fontsize = attributes.get('fontsize', self.default_fontsize)
     89         self.iconOnly = attributes.get('iconOnly', self.default_iconOnly)
     90         self.density = attributes.get('density', self.default_density)
     91         options = attributes.get('options', self.default_options)
     92         select = attributes.get('select', self.default_select)
     93         self.maxVisibleEntries = attributes.get('maxVisibleEntries', self.default_maxVisibleEntries)
     94         self.noChoiceLabel = attributes.Get('noChoiceLabel', self.default_noChoiceLabel)
     95         self.nothingSelectedText = attributes.Get('nothingSelectedText', self.default_nothingSelectedText)
     96         self.color = attributes.get('color', self.default_bgColor)
     97         self.hasClearButton = attributes.get('hasClearButton', self.default_hasClearButton)
     98         self.labelleft = attributes.get('labelleft', self.default_labelleft)
     99         self.OnChange = attributes.get('callback', self.default_callback)
    100         self.Prepare_()
    101         self.SetLabel_(attributes.get('label', self.default_label))
    102         self.LoadOptions(options, select)
    103         if self.adjustWidth and self.align not in ALIGNMENTS_THAT_DONT_SUPPORT_AUTO_WIDTH:
    104             self.AutoAdjustWidth_()
    105         elif self.align == uiconst.TOALL:
    106             self.width = 0
    107         else:
    108             self.width = attributes.get('width', self.default_width)
    109         if self.align in (uiconst.TOLEFT, uiconst.TORIGHT, uiconst.TOALL):
    110             self.height = 0
    111         else:
    112             self.height = styling.get_height(self.density)
    113 
    114     def Prepare_(self):
    115         self.sr.content = Container(parent=self, name='__maincontent')
    116         self.sr.iconParent = Container(parent=self.sr.content, name='iconParent', align=uiconst.TOLEFT, width=24, state=uiconst.UI_HIDDEN)
    117         self.sr.selectedIcon = Icon(name='icon', parent=self.sr.iconParent, align=uiconst.CENTERRIGHT, state=uiconst.UI_DISABLED, width=16, height=16)
    118         self.sr.textclipper = Container(name='__textclipper', parent=self.sr.content, align=uiconst.TOALL, padLeft=8, clipChildren=True)
    119         self.Prepare_SelectedText_()
    120         self.Prepare_Expander_()
    121         self.Prepare_Underlay_()
    122         self.Prepare_Label_()
    123 
    124     def Prepare_SelectedText_(self):
    125         self.sr.selected = eveLabel.EveLabelMedium(name='value', parent=self.sr.textclipper, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, color=self._get_selected_color(), autoFadeSides=16)
    126 
    127     def Prepare_Underlay_(self):
    128         self.sr.backgroundFrame = ComboUnderlay(parent=self, align=uiconst.TOALL)
    129 
    130     def Select(self, animate = True):
    131         self.sr.backgroundFrame.Select()
    132 
    133     def Deselect(self, animate = True):
    134         self.sr.backgroundFrame.Deselect()
    135 
    136     def Disable(self):
    137         self._disabled = True
    138         self.sr.backgroundFrame.SetDisabled()
    139         self._update_selected_color()
    140 
    141     def Enable(self):
    142         self._disabled = False
    143         self.sr.backgroundFrame.SetEnabled()
    144         self._update_selected_color()
    145 
    146     def Prepare_Expander_(self):
    147         self.sr.expanderParent = Container(name='__expanderParent', parent=self.sr.content, align=uiconst.TORIGHT, state=uiconst.UI_DISABLED, width=16, padding=(8, 0, 8, 0), idx=0)
    148         self.sr.expander = GlowSprite(parent=self.sr.expanderParent, align=uiconst.CENTER, texturePath='res:/UI/Texture/Icons/38_16_229.png', pos=(0, 0, 16, 16), opacity=0.8)
    149 
    150     def Prepare_Label_(self):
    151         self.sr.label = eveLabel.EveLabelSmall(text='', parent=self, name='label', top=-13, left=1, state=uiconst.UI_HIDDEN, idx=1, lineSpacing=-0.1)
    152 
    153     def HideText(self):
    154         self.sr.textclipper.state = uiconst.UI_HIDDEN
    155 
    156     def Close(self, *args, **kwds):
    157         self.Cleanup(0)
    158         super(Combo, self).Close()
    159 
    160     def Confirm(self, setfocus = 1):
    161         self._TriggerCallback()
    162         if not self.destroyed:
    163             self.Cleanup(setfocus)
    164 
    165     def OnUp(self, *args):
    166         if not self._Expanded():
    167             self.Expand()
    168         if self._Expanded():
    169             self._comboDropDown().sr.scroll.BrowseNodes(1)
    170 
    171     def OnDown(self, *args):
    172         if not self._Expanded():
    173             self.Expand()
    174         if self._Expanded():
    175             self._comboDropDown().sr.scroll.BrowseNodes(0)
    176 
    177     def SetHint(self, hint):
    178         self.hint = hint
    179 
    180     def SetLabel_(self, label):
    181         if self.labelleft is not None:
    182             self.padLeft = self.labelleft
    183             self.sr.label.left = -self.labelleft
    184             self.sr.label.width = self.labelleft - 6
    185         if label:
    186             self.sr.label.text = label
    187             if self.labelleft is not None:
    188                 self.sr.label.top = 0
    189                 self.sr.label.SetAlign(uiconst.CENTERLEFT)
    190             else:
    191                 self.sr.label.top = -self.sr.label.textheight
    192             self.sr.label.state = uiconst.UI_DISABLED
    193         else:
    194             self.sr.label.state = uiconst.UI_HIDDEN
    195 
    196     def OnSetFocus(self, *args):
    197         if self and not self.destroyed and self.parent and self.parent.name == 'inlines':
    198             if self.parent.parent and self.parent.parent.sr.node:
    199                 browser = GetBrowser(self)
    200                 if browser:
    201                     uthread.new(browser.ShowObject, self)
    202 
    203     def LoadOptions(self, entries, select = None, hints = None):
    204         if entries is None:
    205             raise RuntimeError("'%s' combo option entries to load are None" % self.name)
    206         self.entries = entries
    207         screwed = [ each for each in self.entries if not isinstance(each.label, basestring) ]
    208         if screwed:
    209             raise RuntimeError("'%s' combo option keys are not strings: %s" % (self.name, repr(screwed)))
    210         if not self.entries:
    211             self.entries = [ComboEntryDataText(localization.GetByLabel('/Carbon/UI/Controls/Combo/NoChoices'))]
    212         self.hints = hints
    213         if not select and self.nothingSelectedText:
    214             self.SetNothingSelected()
    215         else:
    216             self._SelectEntry(select)
    217         self.AutoAdjustWidth_()
    218 
    219     @property
    220     def entries(self):
    221         return self._entries
    222 
    223     @entries.setter
    224     def entries(self, value):
    225         self._entries = self._ConvertTuplesToComboEntryData(value)
    226 
    227     def _ConvertTuplesToComboEntryData(self, entries):
    228         ret = []
    229         for entry in entries:
    230             if not isinstance(entry, BaseComboEntryData):
    231                 entry = ComboEntryData(*entry)
    232             ret.append(entry)
    233 
    234         return ret
    235 
    236     def SetNothingSelected(self):
    237         self.selectedValue = None
    238         self.sr.selected.text = self.nothingSelectedText
    239         self._update_selected_color()
    240 
    241     def _SelectEntry(self, select):
    242         selectableEntries = self.GetSelectableEntries()
    243         if not selectableEntries:
    244             return
    245         if select == '__random__':
    246             select = random.choice(selectableEntries).returnValue
    247         elif select is None:
    248             select = selectableEntries[0].returnValue
    249         success = self.SelectItemByValue(select)
    250         if not success:
    251             self.SelectItemByValue(selectableEntries[0].returnValue)
    252 
    253     def _UpdateClearButton(self):
    254         if not self.hasClearButton:
    255             return
    256         if self.selectedValue is not None:
    257             self._CheckConstructClearButton()
    258             self.clearButton.Show()
    259         elif self.clearButton:
    260             self.clearButton.Hide()
    261 
    262     def _CheckConstructClearButton(self):
    263         if not self.clearButton:
    264             self.clearButton = ButtonIcon(parent=self, align=uiconst.CENTERRIGHT, left=24, width=24, height=24, iconSize=16, texturePath=eveicon.close, hint=localization.GetByLabel('UI/Calendar/Hints/Clear'), func=self.OnClearButton, idx=0)
    265 
    266     def OnClearButton(self, *args):
    267         self.SetNothingSelected()
    268         self._UpdateClearButton()
    269         self._TriggerCallback()
    270 
    271     def _TriggerCallback(self):
    272         if self.OnChange:
    273             uthread.new(self.OnChange, self, self.GetKey(), self.GetValue())
    274 
    275     def AutoAdjustWidth_(self):
    276         currentAlign = self.GetAlign()
    277         if self.adjustWidth and self.entries and currentAlign not in ALIGNMENTS_THAT_DONT_SUPPORT_AUTO_WIDTH:
    278             if self.iconOnly:
    279                 width = self.sr.iconParent.left + self.sr.iconParent.padLeft + self.sr.iconParent.width + self.sr.iconParent.padRight + self.sr.expanderParent.left + self.sr.expanderParent.padLeft + self.sr.expanderParent.width + self.sr.expanderParent.padRight
    280                 if self.hasClearButton:
    281                     width += self.clearButton.left + self.clearButton.padLeft + self.clearButton.width + self.clearButton.padRight
    282                 self.width = max(width, 32)
    283             else:
    284                 horizontal_padding = self.sr.textclipper.left + self.sr.textclipper.padLeft + self.sr.textclipper.padRight
    285                 horizontal_padding += self.sr.expanderParent.left + self.sr.expanderParent.padLeft + self.sr.expanderParent.width + self.sr.expanderParent.padRight
    286                 if self.sr.iconParent.display:
    287                     horizontal_padding += self.sr.iconParent.left + self.sr.iconParent.padLeft + self.sr.iconParent.width + self.sr.iconParent.padRight
    288                 if self.hasClearButton:
    289                     horizontal_padding += 24
    290                 max_text_width = max((self.GetTextWidth(entry.label) for entry in self.entries))
    291                 self.width = max(32, max_text_width) + horizontal_padding
    292 
    293     def GetKey(self):
    294         return self.sr.selected.text
    295 
    296     def GetValue(self):
    297         if self.selectedValue is not None:
    298             return self.selectedValue
    299         else:
    300             return
    301 
    302     def GetIndex(self):
    303         if self.sr.selected.text:
    304             i = 0
    305             for entry in self.GetSelectableEntries():
    306                 if entry.label == self.sr.selected.text:
    307                     return i
    308                 i += 1
    309 
    310     def GetSelectedEntry(self):
    311         if self.selectedValue is None:
    312             return
    313         for entry in self.GetSelectableEntries():
    314             if self.selectedValue == entry.returnValue:
    315                 return entry
    316 
    317     def GetSelectableEntries(self):
    318         return [ entry for entry in self.entries if isinstance(entry, ComboEntryData) ]
    319 
    320     def SelectItemByIndex(self, i):
    321         self.SelectItemByLabel(self.GetSelectableEntries()[i].label)
    322 
    323     def SelectItemByLabel(self, label):
    324         for each in self.GetSelectableEntries():
    325             if each.label == label:
    326                 self.UpdateSelectedValue(each)
    327                 return
    328 
    329         raise RuntimeError('LabelNotInEntries', label)
    330 
    331     def SelectItemByValue(self, val, triggerCallback = False):
    332         for each in self.GetSelectableEntries():
    333             if each.returnValue == val:
    334                 self.UpdateSelectedValue(each)
    335                 if triggerCallback:
    336                     self._TriggerCallback()
    337                 return True
    338 
    339         log.warn('Combo has no value %s', val)
    340         return False
    341 
    342     SetValue = SelectItemByValue
    343 
    344     def UpdateSelectedValue(self, entry):
    345         self.selectedValue = entry.returnValue
    346         if not self.iconOnly:
    347             self.sr.selected.text = entry.label
    348             self._update_selected_color()
    349         if entry.icon:
    350             self.sr.selectedIcon.LoadIcon(entry.icon, ignoreSize=True)
    351             self.sr.selectedIcon.rgba = entry.iconColor
    352             self.sr.iconParent.state = uiconst.UI_DISABLED
    353         else:
    354             self.sr.iconParent.state = uiconst.UI_HIDDEN
    355         if self.hasClearButton:
    356             self._UpdateClearButton()
    357 
    358     def UpdateSettings(self):
    359         prefskey = self.prefskey
    360         if prefskey is None:
    361             return
    362         config = prefskey[-1]
    363         prefstype = prefskey[:-1]
    364         s = GetAttrs(settings, *prefstype)
    365         try:
    366             s.Set(config, self.GetValue())
    367         except Exception:
    368             log.error('Failed to assign setting to: %s, %s' % (prefstype, config))
    369 
    370     def _Expanded(self):
    371         return bool(self._comboDropDown and self._comboDropDown())
    372 
    373     def OnMouseEnter(self, *args):
    374         self._hovered = True
    375         self._update_selected_color(duration=uiconst.TIME_ENTRY)
    376         if self._disabled:
    377             return
    378         self.sr.backgroundFrame.OnMouseEnter()
    379         self.sr.expander.OnMouseEnter()
    380         PlaySound(uiconst.SOUND_BUTTON_HOVER)
    381 
    382     def OnMouseExit(self, *args):
    383         self._hovered = False
    384         self.sr.backgroundFrame.OnMouseExit()
    385         self.sr.expander.OnMouseExit()
    386         self._update_selected_color(duration=uiconst.TIME_EXIT)
    387 
    388     def OnMouseDown(self, *args):
    389         self.sr.backgroundFrame.OnMouseDown()
    390 
    391     def OnMouseUp(self, *args):
    392         if self._disabled:
    393             return
    394         self.sr.backgroundFrame.OnMouseUp()
    395 
    396     def OnClick(self, *args):
    397         if len(self._hidden_values) >= len(self.GetSelectableEntries()):
    398             return
    399         if self._disabled:
    400             return
    401         if not self._Expanded():
    402             uthread.new(self.Expand)
    403 
    404     def Prepare_OptionMenu_(self):
    405         ClearMenuLayer()
    406         self.ConstructOptionMenuContainer()
    407         self.ConstructOptionMenuScroll()
    408         self.ConstructOptionMenuBackground()
    409         return (self.optionMenu, self.optionMenu.sr.scroll)
    410 
    411     def ConstructOptionMenuBackground(self):
    412         Frame(bgParent=self.optionMenu, texturePath='res:/UI/Texture/classes/combo/underlay_lower_stroke.png', cornerSize=9, color=eveColor.WHITE[:3] + (0.1,))
    413         BlurredSceneUnderlay(bgParent=self.optionMenu, isInFocus=True, color=Color.HextoRGBA('#ff0b0a12'))
    414 
    415     def ConstructOptionMenuScroll(self):
    416         self.optionMenu.sr.scroll = self._ConstructOptionMenuScroll()
    417         self.optionMenu.sr.scroll.OnKillFocus = self.OnScrollFocusLost
    418         self.optionMenu.sr.scroll.OnSelectionChange = self.OnScrollSelectionChange
    419         self.optionMenu.sr.scroll.Confirm = self.Confirm
    420         self.optionMenu.sr.scroll.OnUp = self.OnUp
    421         self.optionMenu.sr.scroll.OnDown = self.OnDown
    422         self.optionMenu.sr.scroll.OnRight = self.Confirm
    423         self.optionMenu.sr.scroll.OnLeft = self.Confirm
    424         self.optionMenu.sr.scroll.HideBackground()
    425         self.optionMenu.sr.scroll.padding = (1, 0, 1, 1)
    426 
    427     def _ConstructOptionMenuScroll(self):
    428         return Scroll(parent=self.optionMenu, pushContent=False)
    429 
    430     def ConstructOptionMenuContainer(self):
    431         self.optionMenu = Container(parent=uicore.layer.menu, align=uiconst.RELATIVE)
    432 
    433     def GetSelectedIcon(self):
    434         for each in self.GetSelectableEntries():
    435             if each.returnValue == self.selectedValue:
    436                 return each.icon
    437 
    438     def GetEntryClass(self):
    439         return ComboEntry
    440 
    441     def GetTextWidth(self, text):
    442         width, _ = eveLabel.EveLabelMedium.MeasureTextSize(text)
    443         return width
    444 
    445     def GetEntryWidth(self, data):
    446         width = self.GetTextWidth(data['label'])
    447         if data['icon'] is not None:
    448             width += 24
    449         return width
    450 
    451     def GetScrollEntry(self, entryData):
    452         if isinstance(entryData, ComboEntryData):
    453             return self._GetScrollEntryData(entryData)
    454         if isinstance(entryData, ComboEntryDataCaption):
    455             return self._GetScrollEntryDataCaption(entryData)
    456         if isinstance(entryData, ComboEntryDataText):
    457             return self._GetScrollEntryDataText(entryData)
    458         if isinstance(entryData, ComboEntryDataSeparator):
    459             return self._GetScrollEntryDataSeparator(entryData)
    460 
    461     def _GetScrollEntryData(self, entryData):
    462         if not entryData.hint and self.hints:
    463             hint = self.hints.get(entryData.label, '')
    464         else:
    465             hint = entryData.GetHint()
    466         data = {'OnClick': self.OnEntryClick,
    467          'data': (entryData.label, entryData.returnValue),
    468          'label': unicode(entryData.label),
    469          'fontStyle': self.fontStyle,
    470          'fontFamily': self.fontFamily,
    471          'fontPath': self.fontPath,
    472          'fontsize': self.fontsize,
    473          'decoClass': self.GetEntryClass(),
    474          'hideLines': True,
    475          'icon': entryData.icon,
    476          'iconColor': entryData.iconColor,
    477          'indentLevel': entryData.indentLevel,
    478          'loadTooltipFunc': entryData.GetLoadTooltipFunc(),
    479          'hint': hint}
    480         if entryData.returnValue == self.selectedValue:
    481             data['isSelected'] = True
    482         return data
    483 
    484     def _GetScrollEntryDataText(self, entryData):
    485         return {'label': entryData.label,
    486          'text': entryData.label,
    487          'icon': entryData.icon,
    488          'decoClass': Text,
    489          'hint': entryData.GetHint()}
    490 
    491     def _GetScrollEntryDataCaption(self, entryData):
    492         return {'label': entryData.label,
    493          'text': entryData.label,
    494          'icon': entryData.icon,
    495          'decoClass': ComboCaptionEntry,
    496          'hint': entryData.GetHint()}
    497 
    498     def _GetScrollEntryDataSeparator(self, entryData):
    499         return {'label': entryData.label,
    500          'text': entryData.label,
    501          'icon': entryData.icon,
    502          'decoClass': ComboSeparatorEntry,
    503          'hint': entryData.GetHint()}
    504 
    505     def GetMaxEntryWidth(self, scrollEntries, w):
    506         maxWidth = max([ self.GetEntryWidth(entry) for entry in scrollEntries ])
    507         return max(maxWidth + 24, w)
    508 
    509     def GetScrollList(self):
    510         scrolllist = []
    511         if self.noChoiceLabel and len(self.entries) == 1:
    512             data = {'label': eveformat.color(self.noChoiceLabel, TextColor.SECONDARY),
    513              'icon': None,
    514              'selectable': False}
    515             scrollEntry = ScrollEntryNode(**data)
    516             scrolllist.append(scrollEntry)
    517         else:
    518             for entryData in self.entries:
    519                 if entryData.returnValue in self._hidden_values:
    520                     continue
    521                 label = entryData.label
    522                 if not label:
    523                     continue
    524                 data = self.GetScrollEntry(entryData)
    525                 scrollEntry = ScrollEntryNode(**data)
    526                 scrolllist.append(scrollEntry)
    527 
    528         return scrolllist
    529 
    530     def Expand(self, position = None):
    531         if self._expanding:
    532             return
    533         try:
    534             self._expanding = True
    535             PlaySound(uiconst.SOUND_EXPAND)
    536             menu, scroll = self.Prepare_OptionMenu_()
    537             scrolllist = self.GetScrollList()
    538             scroll.LoadContent(contentList=scrolllist)
    539             if position:
    540                 l, t, w, h = position
    541             else:
    542                 l, t, w, h = self.GetAbsolute()
    543             menu.width = self.GetMaxEntryWidth(scrolllist, w)
    544             totalHeight = self.GetScrollHeight(scroll)
    545             menu.height = totalHeight + 2 + scroll.padTop + scroll.padBottom
    546             menu.left = l
    547             self.SetMenuTop(menu, t, h)
    548             self._comboDropDown = weakref.ref(menu)
    549             uthread.new(self.ShowSelected)
    550             if hasattr(self.sr.backgroundFrame, 'Expand'):
    551                 self.sr.backgroundFrame.Expand(totalHeight)
    552             return scroll
    553         finally:
    554             self._expanding = False
    555 
    556     def GetScrollHeight(self, scroll):
    557         return sum([ each.height for each in scroll.sr.nodes[:self.maxVisibleEntries] ])
    558 
    559     def SetMenuTop(self, menu, t, h):
    560         if t + h + menu.height > uicore.desktop.height:
    561             menu.top = t - menu.height
    562         else:
    563             menu.top = t + h
    564 
    565     def GetExpanderIconWidth(self):
    566         if self.sr.expanderParent:
    567             return self.sr.expanderParent.width + self.sr.expanderParent.padLeft + self.sr.expanderParent.padRight
    568         return 0
    569 
    570     def ShowSelected(self, *args):
    571         blue.synchro.Yield()
    572         if self.destroyed or not self._comboDropDown():
    573             return
    574         scroll = self._comboDropDown().sr.scroll
    575         uicore.registry.SetFocus(scroll)
    576         scroll.ScrollToSelectedNode()
    577 
    578     def OnEntryClick(self, entry, *args):
    579         PlaySound(uiconst.SOUND_BUTTON_CLICK)
    580         key, val = entry.sr.node.data
    581         self.SelectItemByValue(val)
    582         self.Cleanup()
    583         if self.OnChange:
    584             self.OnChange(self, key, val)
    585 
    586     def OnComboClose(self, *args):
    587         self.Cleanup(0)
    588 
    589     def Cleanup(self, setfocus = 1):
    590         if self._comboDropDown:
    591             ClearMenuLayer()
    592         self._comboDropDown = None
    593         try:
    594             self.CollapseBackground()
    595         except Exception:
    596             log.exception("Failed to collapse the Combo's background")
    597 
    598         if setfocus:
    599             uicore.registry.SetFocus(self)
    600 
    601     def OnScrollFocusLost(self, *args):
    602         self.Confirm(setfocus=False)
    603 
    604     def CollapseBackground(self):
    605         if hasattr(self.sr.backgroundFrame, 'Collapse'):
    606             self.sr.backgroundFrame.Collapse()
    607 
    608     def OnScrollSelectionChange(self, selected):
    609         if selected:
    610             self.SelectItemByLabel(selected[0].label)
    611 
    612     def OnChar(self, enteredChar, *args):
    613         if enteredChar < 32 and enteredChar != uiconst.VK_RETURN:
    614             return False
    615         if not self._Expanded():
    616             scroll = self.Expand()
    617             scroll.OnChar(enteredChar, *args)
    618         return True
    619 
    620     def Startup(self, *args):
    621         pass
    622 
    623     def _get_selected_color(self):
    624         if self._disabled:
    625             return TextColor.DISABLED
    626         elif self._hovered:
    627             return TextColor.HIGHLIGHT
    628         elif self.selectedValue is None:
    629             return TextColor.SECONDARY
    630         else:
    631             return TextColor.NORMAL
    632 
    633     def _update_selected_color(self, duration = 0.3):
    634         animations.SpColorMorphTo(self.sr.selected, endColor=self._get_selected_color(), duration=duration)
    635 
    636     def HideEntry(self, entry_value):
    637         if entry_value in self._hidden_values:
    638             return
    639         self._hidden_values.append(entry_value)
    640 
    641     def ShowEntry(self, entry_value):
    642         if entry_value in self._hidden_values:
    643             self._hidden_values.remove(entry_value)
    644 
    645 
    646 class SelectCore(Scroll):
    647 
    648     def LoadEntries(self, entries):
    649         scrolllist = []
    650         for entryName, entryValue, selected in entries:
    651             scrolllist.append(ScrollEntryNode(label=entryName, value=entryValue, isSelected=selected))
    652 
    653         self.LoadContent(contentList=scrolllist)
    654 
    655     def GetValue(self):
    656         return [ node.value for node in self.GetSelected() ] or None
    657 
    658     def SetValue(self, val):
    659         if type(val) != list:
    660             val = [val]
    661         for node in self.GetNodes():
    662             if node.value in val:
    663                 self._SelectNode(node)
    664 
    665 
    666 class ComboUnderlay(Container):
    667 
    668     def __init__(self, parent = None, align = uiconst.TOALL):
    669         super(ComboUnderlay, self).__init__(parent=parent, align=align)
    670         self.strokeFrame = Frame(parent=self, texturePath='res:/UI/Texture/classes/combo/underlay_stroke.png', cornerSize=9, color=eveColor.WHITE[:3] + (0.1,))
    671         self.fillFrame = Frame(parent=self, texturePath='res:/UI/Texture/classes/combo/underlay_fill.png', cornerSize=9, color=eveColor.BLACK[:3] + (0.1,))
    672         self.line = Line(parent=Container(parent=self, padding=(8, 0, 8, 0)), align=uiconst.TOBOTTOM, state=uiconst.UI_HIDDEN, height=1, color=eveColor.WHITE[:3] + (0.1,))
    673         self.fill = BlurredSceneUnderlay(parent=self, align=uiconst.TOALL, state=uiconst.UI_HIDDEN, isInFocus=True, color=Color.HextoRGBA('#ff0b0a12'))
    674 
    675     def Expand(self, height):
    676         self.strokeFrame.texturePath = 'res:/UI/Texture/classes/combo/underlay_upper_stroke.png'
    677         self.fillFrame.Hide()
    678         self.line.Show()
    679         self.fill.Show()
    680 
    681     def Collapse(self):
    682         self.strokeFrame.texturePath = 'res:/UI/Texture/classes/combo/underlay_stroke.png'
    683         self.fillFrame.Show()
    684         self.line.Hide()
    685         self.fill.Hide()
    686 
    687     def SetEnabled(self):
    688         pass
    689 
    690     def SetDisabled(self):
    691         pass
    692 
    693     def OnMouseEnter(self):
    694         pass
    695 
    696     def OnMouseExit(self):
    697         pass
    698 
    699     def OnMouseDown(self):
    700         pass
    701 
    702     def OnMouseUp(self):
    703         pass
    704 
    705     def OnWindowAboveSetActive(self):
    706         animations.FadeTo(self.fillFrame, startVal=self.fillFrame.opacity, endVal=0.5, duration=uiconst.TIME_ENTRY)
    707 
    708     def OnWindowAboveSetInactive(self):
    709         animations.FadeTo(self.fillFrame, startVal=self.fillFrame.opacity, endVal=0.1, duration=uiconst.TIME_EXIT)