expandablemenu.py (13471B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\control\expandablemenu.py 2 import random 3 import blue 4 import mathext 5 import uthread 6 from carbon.client.script.environment.AudioUtil import PlaySound 7 from carbonui import uiconst 8 from carbonui.primitives.container import Container 9 from carbonui.primitives.fill import Fill 10 from eve.client.script.ui.control import eveIcon, eveLabel 11 from eve.client.script.ui.control.themeColored import FillThemeColored 12 13 class ExpandableMenuContainer(Container): 14 __guid__ = 'xtriui.ExpandableMenuContainer' 15 sizecallback = None 16 multipleExpanded = False 17 headerApperance = 'default' 18 19 def _OnClose(self): 20 self.sizecallback = None 21 self.sr.menus = None 22 self.sr.menusByLabel = None 23 self.sr.menusByName = {} 24 25 def Load(self, menuData, prefsKey = None): 26 self.sr.menus = [] 27 self.sr.menusByLabel = {} 28 self.sr.menusByName = {} 29 self.singleton = bool(len(menuData) == 1) 30 for data in menuData: 31 menuName = data[9] if len(data) > 9 else None 32 em = ExpandableMenu(parent=self, align=uiconst.TOTOP, height=18, state=uiconst.UI_NORMAL, name=data[9] if len(data) > 9 else data[0], padBottom=4) 33 em.prefsKey = prefsKey 34 self.sr.menus.append(em) 35 self.sr.menusByLabel[data[0]] = em 36 if menuName: 37 self.sr.menusByName[menuName] = em 38 em.Load(*data[:9]) 39 40 if prefsKey and not self.multipleExpanded: 41 active = settings.user.ui.Get('expandableMenu', {}).get(prefsKey, None) 42 if active: 43 for each in self.children: 44 if each.name == active: 45 uthread.new(each.Expand) 46 47 else: 48 uthread.new(self.children[0].Expand) 49 50 def AutoCollapseIfNeeded(self, ignoreMenu = None): 51 if getattr(self, '_autoCollapsing', False): 52 return 53 self._autoCollapsing = True 54 blue.pyos.synchro.SleepWallclock(10) 55 expanded = [] 56 totalHeight = 0 57 for each in self.children: 58 if not each.display: 59 continue 60 totalHeight += each.height + each.padTop + each.padBottom 61 if isinstance(each, ExpandableMenu) and each._expanded and each is not ignoreMenu: 62 expanded.append(each) 63 64 pl, pt, pw, ph = self.GetAbsolute() 65 collapseMenus = [] 66 random.shuffle(expanded) 67 while totalHeight > ph and expanded: 68 collapse = expanded.pop() 69 collapseMenus.append(collapse) 70 totalHeight -= collapse.height 71 72 for each in collapseMenus: 73 each.Collapse() 74 75 self._autoCollapsing = False 76 77 def ExpandMenuByLabel(self, label): 78 if label in self.sr.menusByLabel: 79 self.sr.menusByLabel[label].Expand() 80 81 def ShowMenuByLabel(self, label): 82 return self._ChangeDisplayOfMenu(label, displayOn=True) 83 84 def HideMenuByLabel(self, label): 85 return self._ChangeDisplayOfMenu(label, displayOn=False) 86 87 def IsMenuExpanded(self, menuName): 88 if menuName in self.sr.menusByName: 89 menu = self.sr.menusByName[menuName] 90 return menu.IsExpanded() 91 return False 92 93 def _ChangeDisplayOfMenu(self, label, displayOn = True): 94 panel = self.sr.menusByLabel.get(label) 95 if panel: 96 panel.display = displayOn 97 98 def _OnSizeChange_NoBlock(self, *args, **kwds): 99 Container._OnSizeChange_NoBlock(self, *args, **kwds) 100 for each in self.children: 101 if not each._changing and each._expanded: 102 endHeight = each.GetExpandedHeight() 103 each.height = endHeight 104 each.sr.content.height = endHeight - each.GetCollapsedHeight() - each.sr.content.top 105 106 107 class ExpandableMenu(Container): 108 __guid__ = 'xtriui.ExpandableMenu' 109 default_name = 'ExpandableMenu' 110 default_clipChildren = True 111 112 def AddHeaderContent(self, content, hideOnMaximize = 1): 113 self._hideHeaderContentOnMaximize = hideOnMaximize 114 self.sr.headerContent = content 115 self.sr.headerParent.children.append(content) 116 if self.sr.content.state == uiconst.UI_HIDDEN: 117 self.sr.headerContent.opacity = 1.0 118 elif hideOnMaximize: 119 self.sr.headerContent.opacity = 0.0 120 121 def Load(self, label, content, callback, dropCallback = None, maxHeight = None, headerContent = None, hideHeaderContentOnMaximize = 1, expandedByDefault = 1, uniqueUiName = None): 122 self._changing = False 123 self._break = False 124 self._expanded = False 125 self._maxHeight = maxHeight 126 self._loaded = False 127 self._lastExpandedHeight = None 128 self._hideHeaderContentOnMaximize = hideHeaderContentOnMaximize 129 self.uniqueUiName = uniqueUiName 130 headerParent = Container(parent=self, name='headerParent', align=uiconst.TOTOP, state=uiconst.UI_PICKCHILDREN, height=24, padBottom=-1) 131 self.sr.headerParent = headerParent 132 contentParent = Container(parent=self, name='contentParent', align=uiconst.TOALL, state=uiconst.UI_NORMAL, pos=(0, 1, 0, 1)) 133 self.sr.content = content 134 self.sr.callback = callback 135 if dropCallback: 136 self.OnDropData = dropCallback 137 t = eveLabel.EveLabelMedium(text=label, parent=headerParent, state=uiconst.UI_DISABLED, left=22, align=uiconst.CENTERLEFT, idx=0) 138 self.sr.headerLabel = t 139 self._headerLabel = label 140 expander = eveIcon.Icon(icon='ui_38_16_228', align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, left=4, top=1) 141 expander.SetAlpha(0.8) 142 headerParent.children.append(expander) 143 self.sr.backgroundFrame = Fill(bgParent=headerParent, opacity=0.05) 144 self.sr.expanderIcon = expander 145 headerParent.height = max(20, t.textheight + 2) 146 self._minHeight = headerParent.height 147 if content: 148 contentParent.children.append(content) 149 if headerContent: 150 self.AddHeaderContent(headerContent, hideHeaderContentOnMaximize) 151 if self.parent.multipleExpanded: 152 current = settings.user.ui.Get('multipleExpandableMenu', {}) 153 if self.prefsKey in current and self.name in current[self.prefsKey]: 154 lastHeight, expanded = current[self.prefsKey][self.name] 155 if expanded: 156 uthread.new(self.Expand, lastHeight, startup=True) 157 return 158 elif expandedByDefault: 159 uthread.new(self.Expand, startup=True) 160 return 161 self.Collapse(startup=1) 162 self._loaded = True 163 164 def SetHeader(self, newlabel, addon = 0, hint = None): 165 if addon: 166 newlabel = self._headerLabel + newlabel 167 self.sr.headerLabel.text = '<b>' + newlabel + '</b>' 168 if hint: 169 self.sr.headerLabel.state = uiconst.UI_NORMAL 170 self.sr.headerLabel.hint = hint 171 self.sr.headerLabel.OnClick = self.OnClick 172 else: 173 self.sr.headerLabel.state = uiconst.UI_DISABLED 174 175 def OnClick(self, *args): 176 if not self._changing: 177 if self._expanded: 178 PlaySound(uiconst.SOUND_COLLAPSE) 179 self.Collapse() 180 else: 181 PlaySound(uiconst.SOUND_EXPAND) 182 self.Expand() 183 184 def OnMouseEnter(self, *args): 185 PlaySound(uiconst.SOUND_BUTTON_HOVER) 186 187 def IsExpanded(self): 188 return self._expanded 189 190 def Expand(self, lastHeight = None, time = None, startup = False, *args): 191 if self._changing: 192 return 193 self._changing = True 194 if not self.parent.multipleExpanded: 195 for each in self.parent.children[:]: 196 if isinstance(each, ExpandableMenu) and each is not self: 197 uthread.new(each.Collapse) 198 199 if self.sr.callback: 200 self.sr.callback(not self._loaded) 201 self.sr.content.align = uiconst.TOTOP 202 self._loaded = True 203 sh = self.height 204 endHeight = self.GetExpandedHeight() 205 self.sr.expanderIcon.LoadIcon('ui_38_16_229') 206 self.sr.content.state = uiconst.UI_NORMAL 207 self.sr.content.height = endHeight - self.GetCollapsedHeight() - self.sr.content.top 208 if time == -1: 209 self.height = endHeight 210 self.sr.content.opacity = 1.0 211 if self.parent.sizecallback: 212 self.parent.sizecallback(self) 213 else: 214 start, ndt = blue.os.GetWallclockTime(), 0.0 215 time = time or 250.0 216 while ndt != 1.0: 217 ndt = max(ndt, min(blue.os.TimeDiffInMs(start, blue.os.GetWallclockTime()) / time, 1.0)) 218 self.height = sh + int((endHeight - sh) * ndt) 219 self.sr.content.opacity = 1.0 * ndt 220 if self.sr.headerContent and self._hideHeaderContentOnMaximize: 221 self.sr.headerContent.opacity = 1.0 - 1.0 * ndt 222 if self.parent.sizecallback: 223 self.parent.sizecallback(self) 224 blue.pyos.synchro.Yield() 225 if self.destroyed or self._break: 226 break 227 228 if self.destroyed: 229 return 230 self.height = endHeight 231 self._changing = False 232 self._break = False 233 self._expanded = True 234 self._lastExpandedHeight = self.height 235 self.sr.content.opacity = 1.0 236 if self.sr.headerContent and self._hideHeaderContentOnMaximize: 237 self.sr.headerContent.opacity = 0.0 238 if startup: 239 uthread.new(self.parent.AutoCollapseIfNeeded) 240 else: 241 uthread.new(self.parent.AutoCollapseIfNeeded, ignoreMenu=self) 242 self.Register() 243 if not startup: 244 sm.ScatterEvent('OnExpandableMenuItemExpanded', self.name) 245 246 def Register(self): 247 if self.prefsKey: 248 if self.parent.multipleExpanded: 249 current = settings.user.ui.Get('multipleExpandableMenu', {}) 250 if self.prefsKey not in current: 251 current[self.prefsKey] = {} 252 current[self.prefsKey][self.name] = (self._lastExpandedHeight, self._expanded) 253 settings.user.ui.Set('multipleExpandableMenu', current) 254 else: 255 current = settings.user.ui.Get('expandableMenu', {}) 256 if self._expanded: 257 current[self.prefsKey] = self.name 258 elif self.prefsKey in current and current[self.prefsKey] == self.name: 259 del current[self.prefsKey] 260 settings.user.ui.Set('expandableMenu', current) 261 262 def Collapse(self, startup = 0): 263 if self._changing: 264 self._break = True 265 self._changing = True 266 sh = self.height 267 endHeight = self.GetCollapsedHeight() 268 pl, pt, pw, ph = self.parent.GetAbsolute() 269 self.sr.expanderIcon.LoadIcon('ui_38_16_228') 270 if not startup: 271 if self.sr.headerContent: 272 sho = self.sr.headerContent.opacity 273 start, ndt = blue.os.GetWallclockTime(), 0.0 274 time = 250.0 275 while ndt != 1.0: 276 ndt = max(ndt, min(blue.os.TimeDiffInMs(start, blue.os.GetWallclockTime()) / time, 1.0)) 277 self.height = sh - int((sh - endHeight) * ndt) 278 self.sr.content.opacity = 1.0 - 1.0 * ndt 279 if self.sr.headerContent and self._hideHeaderContentOnMaximize: 280 self.sr.headerContent.opacity = mathext.lerp(sho, 1.0, ndt) 281 if self.parent.sizecallback: 282 self.parent.sizecallback(self) 283 blue.pyos.synchro.Yield() 284 if self.destroyed or self._break: 285 break 286 287 if self.destroyed: 288 return 289 self._changing = False 290 self._break = False 291 self.height = endHeight 292 self._expanded = False 293 self.sr.content.state = uiconst.UI_HIDDEN 294 self.sr.content.opacity = 0.0 295 if self.sr.headerContent: 296 self.sr.headerContent.opacity = 1.0 297 if not startup: 298 self.Register() 299 sm.ScatterEvent('OnExpandableMenuItemCollapsed', self.name) 300 301 def GetCollapsedHeight(self): 302 return self._minHeight 303 304 def GetExpandedHeight(self): 305 endHeight = self.GetMaxHeight() 306 if not endHeight and not self.parent.multipleExpanded: 307 pw, ph = self.parent.GetAbsoluteSize() 308 total = 0 309 for each in self.parent.children: 310 if isinstance(each, ExpandableMenu): 311 total += each.GetCollapsedHeight() 312 313 endHeight = ph - total 314 return endHeight 315 316 def GetMaxHeight(self): 317 if hasattr(self.sr.content, 'GetTotalHeight'): 318 totalHeight = self.sr.content.top + self.sr.content.GetTotalHeight() + self.GetCollapsedHeight() + 5 319 if self._maxHeight: 320 return min(totalHeight, self._maxHeight) 321 return totalHeight 322 if hasattr(self.sr.content, 'GetContentHeight'): 323 totalHeight = self.sr.content.top + self.sr.content.GetContentHeight() + self.GetCollapsedHeight() + 5 324 if self._maxHeight: 325 return min(totalHeight, self._maxHeight) 326 return totalHeight 327 return self._maxHeight