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

stack.py (20646B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\window\stack.py
      2 import logging
      3 import uthread
      4 import uthread2
      5 from carbonui import uiconst
      6 from carbonui.primitives.base import ReverseScaleDpi
      7 from carbonui.control.contextMenu.menuData import MenuData
      8 from carbonui.decorative.divider_line import DividerLine
      9 from carbonui.primitives.container import Container
     10 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     11 from carbonui.uicore import uicore
     12 from carbonui.util.various_unsorted import Transplant
     13 from carbonui.control.window import Window
     14 from carbonui.window.header.base import WindowHeaderBase
     15 from carbonui.control.tab import Tab
     16 from carbonui.control.tabGroup import TabGroup
     17 from eve.client.script.ui.shared import closeWindowStackPrompt
     18 from menu import MenuLabel
     19 log = logging.getLogger(__name__)
     20 PREFERRED_IDX_IN_STACK_CONFIG = 'preferredIdxInStack3'
     21 
     22 class WindowStack(Window):
     23 
     24     def ApplyAttributes(self, attributes):
     25         self._initializingStack = True
     26         self.contentCont = None
     27         self.tabsCont = None
     28         self._tabGroup = None
     29         self._detaching = 0
     30         self._inserting = 0
     31         super(WindowStack, self).ApplyAttributes(attributes)
     32         self.ConstructContentParent()
     33         self._initializingStack = False
     34 
     35     @property
     36     def compact(self):
     37         wnd = self.GetActiveWindow()
     38         return wnd and wnd.compact
     39 
     40     @compact.setter
     41     def compact(self, value):
     42         wnd = self.GetActiveWindow()
     43         if wnd and wnd.compact != value:
     44             wnd.compact = value
     45             self._emit_on_compact_mode_changed()
     46 
     47     @property
     48     def compactable(self):
     49         wnd = self.GetActiveWindow()
     50         return wnd and wnd.compactable
     51 
     52     @property
     53     def killable(self):
     54         return all((window.killable for window in self.GetWindows()))
     55 
     56     @property
     57     def is_stack(self):
     58         return True
     59 
     60     def _on_header_tab_selected(self, tab_id, old_tab_id):
     61         old_window = old_tab_id
     62         new_window = tab_id
     63         if old_window is None or new_window is not None and new_window.compact != old_window.compact:
     64             self._emit_on_compact_mode_changed()
     65         self._update_window_controls()
     66         self._update_window_caption()
     67 
     68     def ConstructContentParent(self):
     69         self.contentCont = Container(name='__content', parent=self.sr.maincontainer, align=uiconst.TOALL, idx=self.sr.headerParent.GetOrder() + 1)
     70 
     71     def Prepare_LoadingIndicator_(self):
     72         self.sr.loadingIndicator = None
     73 
     74     def Prepare_ScaleAreas_(self):
     75         super(WindowStack, self).Prepare_ScaleAreas_()
     76         for wnd in self.GetWindows():
     77             wnd.Prepare_ScaleAreas_()
     78 
     79     def Prepare_Header_(self):
     80         self.header = WindowStackHeader(callback=self._on_header_tab_selected)
     81 
     82     def CheckStackWith(self, dropWnd):
     83         if self.closing:
     84             return
     85         if not self.IsStackable():
     86             return
     87         if self.sr.modalParent is not None or dropWnd.sr.modalParent is not None or dropWnd == self:
     88             return
     89         if not isinstance(dropWnd, WindowStack):
     90             self.InsertWnd(dropWnd, 0, 1, is_dropping=True)
     91         else:
     92             for wnd in dropWnd.GetWindows()[:]:
     93                 self.InsertWnd(wnd, 0, 1, is_dropping=True)
     94 
     95             dropWnd.Close()
     96         uicore.registry.SetFocus(self)
     97 
     98     def Check(self, updatewnd = 0, autoselecttab = 1, checknone = 0):
     99         if self.closing:
    100             return
    101         myWindows = self.GetWindows()
    102         if checknone and len(myWindows) == 0:
    103             self.Close()
    104             return
    105         self.SetMinWH()
    106         tabs = []
    107         for wnd in myWindows:
    108             if wnd is None or wnd.destroyed:
    109                 continue
    110             tabs.append([wnd.caption or '',
    111              wnd,
    112              self,
    113              wnd])
    114             wnd.HideBackground()
    115             wnd.state = uiconst.UI_PICKCHILDREN
    116 
    117         self._UpdateWindowStates(myWindows)
    118         if len(tabs):
    119             self.header.update_tabs(tabs, auto_select=bool(autoselecttab))
    120             all_tabs = self.header.tab_group.GetTabs()
    121             if all_tabs:
    122                 for i in xrange(len(all_tabs)):
    123                     tab = all_tabs[i]
    124                     wnd = myWindows[i]
    125                     if wnd.isBlinking:
    126                         tab.Blink()
    127 
    128         if not self.active and any((window.active for window in myWindows)):
    129             self.SetActive()
    130 
    131     def _update_window_caption(self):
    132         active_window = self.GetActiveWindow()
    133         if active_window:
    134             self.caption = active_window.caption
    135         else:
    136             self.caption = ''
    137 
    138     def _UpdateWindowStates(self, myWindows):
    139         if any((wnd.IsOverlayed() for wnd in myWindows)):
    140             self.SetOverlayed()
    141         else:
    142             self.SetNotOverlayed()
    143         if any((wnd.IsLightBackgroundEnabled() for wnd in myWindows)):
    144             self.EnableLightBackground()
    145         else:
    146             self.DisableLightBackground()
    147 
    148     def IsLightBackgroundConfigurable(self):
    149         return all((wnd.IsLightBackgroundConfigurable() for wnd in self.GetWindows()))
    150 
    151     def EnableLightBackground(self):
    152         super(WindowStack, self).EnableLightBackground()
    153         for wnd in self.GetWindows():
    154             wnd.EnableLightBackground()
    155 
    156     def DisableLightBackground(self):
    157         super(WindowStack, self).DisableLightBackground()
    158         for wnd in self.GetWindows():
    159             wnd.DisableLightBackground()
    160 
    161     def SetOverlayed(self):
    162         super(WindowStack, self).SetOverlayed()
    163         for wnd in self.GetWindows():
    164             wnd.SetOverlayed()
    165 
    166     def SetNotOverlayed(self):
    167         super(WindowStack, self).SetNotOverlayed()
    168         for wnd in self.GetWindows():
    169             wnd.SetNotOverlayed()
    170 
    171     def GetMenu(self, *args):
    172         menu = []
    173         if self.IsKillable():
    174             menu.append((MenuLabel('/Carbon/UI/Controls/Window/CloseWindowStack'), self.CloseByUser))
    175         if not self.stacked and self.IsMinimizable():
    176             if self.state == uiconst.UI_NORMAL:
    177                 menu.append((MenuLabel('/Carbon/UI/Controls/Window/MinimizeWindowStack'), self.ToggleMinimize))
    178             else:
    179                 menu.append((MenuLabel('/Carbon/UI/Controls/Window/MaximizeWindowStack'), self.ToggleMinimize))
    180         return menu
    181 
    182     def GetMenuMoreOptions(self):
    183         wnd = self.GetActiveWindow()
    184         if wnd:
    185             return wnd.GetMenuMoreOptions()
    186         else:
    187             return MenuData()
    188 
    189     def get_wnd_menu_unique_name(self):
    190         wnd = self.GetActiveWindow()
    191         if wnd:
    192             return wnd.get_wnd_menu_unique_name()
    193         else:
    194             return None
    195 
    196     def GetCustomHeaderButtons(self):
    197         wnd = self.GetActiveWindow()
    198         if wnd:
    199             return wnd.GetCustomHeaderButtons()
    200         else:
    201             return []
    202 
    203     def GetWindowLinkData(self):
    204         wnd = self.GetActiveWindow()
    205         if wnd:
    206             return wnd.GetWindowLinkData()
    207         else:
    208             return None
    209 
    210     def RemoveWnd(self, wnd, grab, correctpos = 1, idx = 0, dragging = 0, check = 1):
    211         if wnd.parent != self.contentCont:
    212             return
    213         if hasattr(wnd, 'OnTabSelect'):
    214             uthread.worker('WindowStack::RemoveWnd', wnd.OnTabSelect)
    215         wnd._detaching = True
    216         Transplant(wnd, self.parent, idx)
    217         if hasattr(wnd, '_stack_cacheContents'):
    218             wnd.cacheContents = wnd._stack_cacheContents
    219         self.RemovePreferredIdx(self.windowID, wnd.windowID)
    220         self._SetRemovedWindowSize(wnd)
    221         wnd.RegisterStackID(None)
    222         wnd.OnStackRemove(correctpos, dragging, grab)
    223         if check:
    224             self.Check()
    225         wnd._detaching = False
    226         wnd._dragging = dragging
    227         myWindows = self.GetWindows()
    228         if len(myWindows) == 1 and not self.IsCollapsed():
    229             w = myWindows[0]
    230             aL, aT, aW, aH = self.GetAbsolute()
    231             x, y = aL, aT
    232             self.RemoveWnd(w, (0, 0), 1, 1, check=0)
    233             self.RemovePreferredIdx(self.windowID, w)
    234             w.left, w.top = x, y
    235             sm.ChainEvent('ProcessWindowUnstacked', wnd, self)
    236             return
    237         if len(self.GetWindows()) == 0:
    238             self.Close()
    239         sm.ChainEvent('ProcessWindowUnstacked', wnd, self)
    240 
    241     def CloseByUser(self, *args):
    242         if len(self.GetWindows()) > 1:
    243             response = closeWindowStackPrompt.ask()
    244             if response == closeWindowStackPrompt.Response.close_all:
    245                 for window in self.GetWindows():
    246                     window.CloseByUser(*args)
    247 
    248             elif response == closeWindowStackPrompt.Response.close_current:
    249                 self.GetActiveWindow().CloseByUser(*args)
    250         else:
    251             canCloseStack = True
    252             for wnd in self.GetWindows()[:]:
    253                 canClose = wnd.CloseByUser()
    254                 if canClose is False:
    255                     canCloseStack = False
    256 
    257             if canCloseStack:
    258                 super(WindowStack, self).CloseByUser(*args)
    259 
    260     def _SetRemovedWindowSize(self, wnd):
    261         wnd.width = wnd._fixedWidth or self.width
    262         wnd.height = wnd._fixedHeight or self.height
    263         wnd.CheckMaxSize()
    264 
    265     def RegisterPositionAndSize(self, key = None, windowID = None):
    266         super(WindowStack, self).RegisterPositionAndSize(key, windowID)
    267         for each in self.GetWindows():
    268             each.RegisterPositionAndSize()
    269 
    270     def GetCollapsedHeight(self):
    271         _, margin_top, _, margin_bottom = self.GetWindowBorderPadding()
    272         _, border_top, _, border_bottom = self.GetWindowBorderSize()
    273         return margin_top + border_top + self.header_height + border_bottom + margin_bottom
    274 
    275     @classmethod
    276     def Reload(cls, instance):
    277         pass
    278 
    279     def GetCaption(self, update = 1):
    280         caption = ''
    281         for wnd in self.GetWindows():
    282             if wnd is not None and not wnd.destroyed:
    283                 caption = '%s%s-' % (caption, wnd.GetCaption(update))
    284 
    285         if caption != '':
    286             return caption[:-1]
    287         return caption
    288 
    289     def OnCollapsed(self, *args):
    290         self.contentCont.display = False
    291         for wnd in self.GetWindows():
    292             wnd.OnCollapsed(*args)
    293 
    294     def OnExpanded(self, *args):
    295         self.contentCont.display = True
    296         for wnd in self.GetWindows():
    297             wnd.OnExpanded(*args)
    298 
    299     def OnEndScale_(self, *args):
    300         for wnd in self.GetWindows():
    301             if wnd.state == uiconst.UI_PICKCHILDREN:
    302                 wnd._emit_on_end_scale()
    303                 wnd.OnEndScale_(wnd)
    304 
    305     def OnStartScale_(self, wnd, *args):
    306         for wnd in self.GetWindows():
    307             if wnd.state == uiconst.UI_PICKCHILDREN:
    308                 wnd._emit_on_start_scale()
    309 
    310     def InsertWnd(self, wnd, adjustlocation = 1, show = 0, hilite = 0, is_dropping = False):
    311         while self._initializingStack:
    312             uthread2.Yield()
    313 
    314         previous_window = self.GetActiveWindow()
    315         windowID = getattr(wnd, 'windowID', None)
    316         preferredIdx = self.GetAndRegisterPreferredIdxInStack(self.windowID, windowID, is_dropping)
    317         self._inserting = True
    318         l, t, mywidth, myheight = self.GetAbsolute()
    319         if not len(self.GetWindows()) and adjustlocation:
    320             log.info('WindowStack initing, taking size from %s %s %s %s %s %s' % (wnd.windowID,
    321              wnd.left,
    322              'l,t,w,h',
    323              wnd.top,
    324              wnd.width,
    325              wnd.height))
    326             self.width = wnd.width
    327             if not self.IsCollapsed():
    328                 self.height = wnd.height
    329             self.left = wnd.left
    330             self.top = wnd.top
    331         if wnd.IsCollapsed():
    332             wnd.Expand()
    333         wnd.SetParent(self.contentCont, idx=preferredIdx)
    334         wnd.OnStackInsert(self)
    335         self.Check(0, show != 1)
    336         if show:
    337             self.ShowWnd(wnd, hilite, previous_window=previous_window)
    338         wnd.RegisterStackID(self)
    339         self.CleanupParent('snapIndicator')
    340         self._inserting = False
    341         if self.IsMinimized():
    342             self.Maximize()
    343         wnd.Prepare_ScaleAreas_()
    344         sm.ChainEvent('ProcessWindowStacked', wnd, self)
    345         self.UpdateIntersectionBackground()
    346 
    347     def SetMinWH(self):
    348         allMinW = 0
    349         allMinH = 0
    350         for each in self.GetWindows():
    351             allMinW = max(allMinW, each.minsize[0])
    352             allMinH = max(allMinH, each.minsize[1])
    353 
    354         if self.IsCollapsed():
    355             self.minsize = [allMinW, allMinH]
    356         else:
    357             self.SetMinSize([allMinW, allMinH])
    358 
    359     def ShowWnd(self, wnd, hilite = 0, previous_window = None):
    360         if wnd not in self.GetWindows() or wnd is None or wnd.destroyed:
    361             return
    362         tab_group = self.GetTabGroup()
    363         if tab_group is not None:
    364             tab_group.ShowPanel(wnd, hilite)
    365             self._on_header_tab_selected(tab_id=wnd, old_tab_id=previous_window)
    366 
    367     def GetActiveWindow(self):
    368         if self.closing:
    369             return
    370         tab_group = self.GetTabGroup()
    371         if tab_group is not None:
    372             return tab_group.GetSelectedPanel()
    373 
    374     def IsResizeable(self):
    375         if self.IsLocked():
    376             return False
    377         for wnd in self.GetWindows():
    378             if not wnd.IsResizeable():
    379                 return False
    380 
    381         return True
    382 
    383     def IsMinimizable(self):
    384         for wnd in self.GetWindows():
    385             if not wnd.IsMinimizable():
    386                 return False
    387 
    388         return True
    389 
    390     def Compact(self):
    391         wnd = self.GetActiveWindow()
    392         if wnd:
    393             wnd.compact = True
    394 
    395     def UnCompact(self):
    396         wnd = self.GetActiveWindow()
    397         if wnd:
    398             wnd.compact = False
    399 
    400     def Detach(self, wnd, grab):
    401         if self.IsLocked():
    402             return False
    403         if wnd is not None and not wnd.destroyed and not getattr(wnd, '_detaching', 0):
    404             uicore.registry.SetFocus(uicore.desktop)
    405             self._detaching = True
    406             self.RemoveWnd(wnd, grab, 1, 0, 1)
    407             if not self.destroyed:
    408                 self._detaching = False
    409             uicore.registry.SetFocus(wnd)
    410             return 1
    411 
    412     def Load(self, wnd):
    413         if self.destroyed:
    414             return
    415         if self.IsCollapsed() and not self._detaching and not self._inserting:
    416             self.Expand()
    417         for _wnd in self.GetWindows():
    418             if _wnd is not wnd:
    419                 _wnd.state = uiconst.UI_HIDDEN
    420 
    421         wnd.state = uiconst.UI_PICKCHILDREN
    422 
    423     def GetMinWidth(self, checkgroup = 1):
    424         trueMinWidth = self.minsize[0]
    425         for wnd in self.GetWindows():
    426             trueMinWidth = max(wnd.GetMinWidth(), trueMinWidth)
    427 
    428         return trueMinWidth
    429 
    430     def GetMinHeight(self):
    431         trueMinHeight = self.minsize[1]
    432         for wnd in self.GetWindows():
    433             trueMinHeight = max(wnd.GetMinHeight(), trueMinHeight)
    434 
    435         return trueMinHeight
    436 
    437     def OnStartMaximize_(self, *args):
    438         for wnd in self.GetWindows():
    439             wnd.OnStartMaximize_(wnd)
    440 
    441     def OnEndMaximize_(self, *args):
    442         for wnd in self.GetWindows():
    443             wnd.OnEndMaximize_(wnd)
    444 
    445     def OnStartMinimize_(self, *args):
    446         for wnd in self.GetWindows():
    447             wnd.OnStartMinimize_(wnd)
    448 
    449     def OnEndMinimize_(self, *args):
    450         for wnd in self.GetWindows():
    451             wnd.OnEndMinimize_(wnd)
    452 
    453     def OnResize_(self, *args):
    454         for wnd in self.GetWindows():
    455             wnd._OnResize()
    456 
    457     def GetWindows(self):
    458         if self.contentCont is None:
    459             return []
    460         else:
    461             return [ each for each in self.contentCont.children if isinstance(each, Window) ]
    462 
    463     def RegisterPreferredIdxInStack(self, stackID, windowID, idx = -1):
    464         allPreferred = settings.char.windows.Get(PREFERRED_IDX_IN_STACK_CONFIG, {})
    465         stackInfo = allPreferred.get(stackID, {})
    466         stackInfo[windowID] = idx
    467         allPreferred[stackID] = stackInfo
    468         settings.char.windows.Set(PREFERRED_IDX_IN_STACK_CONFIG, allPreferred)
    469 
    470     def GetAndRegisterPreferredIdxInStack(self, stackID, windowID, is_dropping = False):
    471         allPreferred = settings.char.windows.Get(PREFERRED_IDX_IN_STACK_CONFIG, {})
    472         stackInfo = allPreferred.get(stackID, {})
    473         preferredIdx = stackInfo.get(windowID, -1)
    474         if is_dropping:
    475             tab_index = 0
    476             tabs = self.header.tab_group.GetTabs()
    477             cursorX, cursorY = uicore.uilib.cursorPos
    478             cursorX = ReverseScaleDpi(cursorX)
    479             cursorY = ReverseScaleDpi(cursorY)
    480             for tab in tabs:
    481                 if cursorX <= tab.absoluteRight and cursorY >= self.header.absoluteTop and cursorY <= self.header.absoluteBottom:
    482                     preferredIdx = tab_index
    483                     break
    484                 tab_index += 1
    485 
    486         if preferredIdx < 0:
    487             if len(self.contentCont.children) < 1:
    488                 preferredIdx = 0
    489             else:
    490                 preferredIdx = len(self.contentCont.children)
    491             self.RegisterPreferredIdxInStack(stackID, windowID, preferredIdx)
    492         return preferredIdx
    493 
    494     def RemovePreferredIdx(self, stackID, windowID):
    495         allPreferred = settings.char.windows.Get(PREFERRED_IDX_IN_STACK_CONFIG, {})
    496         stackInfo = allPreferred.pop(stackID, None)
    497         if stackInfo:
    498             oldPreferredIdx = stackInfo.pop(windowID, None)
    499             openWndIDs = {x.windowID for x in uicore.registry.GetWindows()}
    500             stackInfoAsList = stackInfo.items()
    501             stackInfoAsList.sort(key=lambda x: x[1])
    502             newStackInfo = {}
    503             for eachWndID, eachIdx in stackInfoAsList:
    504                 if eachWndID in openWndIDs and eachIdx > oldPreferredIdx:
    505                     eachIdx -= max(0, 1)
    506                 newStackInfo[eachWndID] = eachIdx
    507 
    508             allPreferred[stackID] = newStackInfo
    509         settings.char.windows.Set(PREFERRED_IDX_IN_STACK_CONFIG, allPreferred)
    510 
    511     def GetTabGroup(self):
    512         if self.header is not None and self.header.tab_group is not None and not self.header.tab_group.destroyed:
    513             return self.header.tab_group
    514 
    515     def ForceUpdateSelectedTabSetting(self):
    516         tabGroup = self.GetTabGroup()
    517         if not tabGroup:
    518             return
    519         tab = tabGroup.GetSelectedTab()
    520         if not tab:
    521             return
    522         tab.UpdateSelectedSettings(True)
    523 
    524 
    525 class WindowStackTab(Tab):
    526 
    527     def GetMenu(self):
    528         wnd = self.GetPanel()
    529         return wnd.GetMenu()
    530 
    531 
    532 class WindowStackHeader(WindowHeaderBase, ContainerAutoSize):
    533 
    534     def __init__(self, callback = None):
    535         self._callback = callback
    536         self._tab_group = None
    537         self._line = None
    538         super(WindowStackHeader, self).__init__(alignMode=uiconst.TOTOP, clipChildren=True)
    539 
    540     @property
    541     def tab_group(self):
    542         return self._tab_group
    543 
    544     def mount(self, window):
    545         super(WindowStackHeader, self).mount(window)
    546         self._update_padding(window)
    547         self._layout(window)
    548         window.on_header_inset_changed.connect(self._on_header_inset_changed)
    549         window.on_collapsed_changed.connect(self._on_window_collapsed_changed)
    550 
    551     def unmount(self, window):
    552         super(WindowStackHeader, self).unmount(window)
    553         window.on_header_inset_changed.disconnect(self._on_header_inset_changed)
    554         window.on_collapsed_changed.disconnect(self._on_window_collapsed_changed)
    555         self._clear()
    556 
    557     def update_tabs(self, tabs, auto_select = True):
    558         if self._tab_group is None:
    559             raise RuntimeError("Attempting to update header when it's not mounted")
    560         self._tab_group.ClearTabs()
    561         for tab_data in tabs:
    562             self._tab_group.AddTab(*tab_data)
    563 
    564         if auto_select:
    565             self._tab_group.AutoSelect()
    566 
    567     def _layout(self, window):
    568         pad_left, pad_right = window.header_inset
    569         self._tab_group = TabGroup(name='tabparent', parent=self, padding=(pad_left,
    570          0,
    571          pad_right,
    572          0), groupID=window.name, tabClass=WindowStackTab, callback=self._callback, show_line=False)
    573         self._line = DividerLine(parent=self, align=uiconst.TOBOTTOM_NOPUSH)
    574 
    575     def _clear(self):
    576         self.Flush()
    577         self._tab_group = None
    578         self._line = None
    579 
    580     def _on_header_inset_changed(self, window):
    581         self._update_padding(window)
    582 
    583     def _on_window_collapsed_changed(self, window):
    584         self._line.display = False if window.collapsed else True
    585 
    586     def _update_padding(self, window):
    587         if self._tab_group is not None:
    588             self._tab_group.padLeft, self._tab_group.padRight = window.header_inset