historyBar.py (15952B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\control\historyBar.py 2 import carbonui.const as uiconst 3 import uthread 4 from carbon.client.script.environment.AudioUtil import PlaySound 5 from carbon.common.script.util import timerstuff 6 from carbonui.primitives.container import Container 7 from carbonui.primitives.fill import Fill 8 from carbonui.primitives.frame import Frame 9 from carbonui.primitives.sprite import Sprite 10 from carbonui.uicore import uicore 11 from carbonui.control.buttonIcon import ButtonIcon 12 from eveexceptions import ExceptionEater 13 from menu import MenuLabel 14 from signals import Signal 15 from signals.signalUtil import ChangeSignalConnect 16 UPDATE_CODE_INIT = 1 17 UPDATE_CODE_ADD = 2 18 UPDATE_CODE_REMOVE = 3 19 UPDATE_CODE_CLEAR = 4 20 UPDATE_CODE_SELECTED = 5 21 UPDATE_CODE_CORRECTION = 6 22 23 class HistoryBar(Container): 24 default_name = 'historyBar' 25 default_left = 0 26 default_top = 0 27 default_width = 400 28 default_height = 17 29 default_align = uiconst.CENTER 30 default_btnSize = 9 31 default_bitWidth = 7 32 default_bitGap = 3 33 34 def ApplyAttributes(self, attributes): 35 Container.ApplyAttributes(self, attributes) 36 self.bitChangeCheck = attributes.bitChangeCheck 37 self.btnSize = attributes.get('btnSize', self.default_btnSize) 38 self.callback = attributes.callback 39 self.bitHintFunc = attributes.bitHintFunc 40 self.isAllowedToLoadFunc = attributes.isAllowedToLoadFunc 41 self.historyController = attributes.get('historyController', HistoryController()) 42 self.bitHeight = attributes.get('bitHeight', self.height - 6) 43 self.bitWidth = attributes.get('bitWidth', self.default_bitWidth) 44 self.bitGap = attributes.get('bitGap', self.default_bitGap) 45 self._scrolling = False 46 self._mouseOffset = 0 47 self.ConstructButtons() 48 self.ConstructBar() 49 self.UpdateHistory(updateCode=UPDATE_CODE_INIT) 50 self.ChangeSignalConnection() 51 52 def ChangeSignalConnection(self, connect = True): 53 signalAndCallback = [(self.historyController.on_history_updated, self.OnHistoryUpdated), (self.historyController.on_history_correction_triggered, self.OnCorrectPosition)] 54 ChangeSignalConnect(signalAndCallback, connect) 55 56 def OnHistoryUpdated(self, updateCode = None): 57 self.UpdateHistory(updateCode=updateCode) 58 59 def ConstructButtons(self): 60 fullSize = self.btnSize + 10 61 leftBtnCont = Container(name='leftBtnCont', parent=self, align=uiconst.TOLEFT, width=fullSize, opacity=0.75) 62 self.leftBtn = ButtonIcon(name='leftBtn', parent=leftBtnCont, align=uiconst.CENTERRIGHT, width=fullSize, height=fullSize, iconSize=self.btnSize, texturePath='res:/ui/texture/shared/arrows/arrowLeft.png', func=self.OnButtonClicked, args=(-1,), iconClass=Sprite) 63 rightBtnCont = Container(name='leftBtnCont', parent=self, align=uiconst.TORIGHT, width=fullSize, opacity=0.75) 64 self.rightBtn = ButtonIcon(name='rightBtn', parent=rightBtnCont, align=uiconst.CENTERLEFT, width=fullSize, height=fullSize, iconSize=self.btnSize, texturePath='res:/ui/texture/shared/arrows/arrowRight.png', func=self.OnButtonClicked, args=(1,), iconClass=Sprite) 65 66 def ConstructBar(self): 67 self.mainPar = Container(name='mainPar', parent=self, clipChildren=True, state=uiconst.UI_NORMAL) 68 Frame(parent=self.mainPar, color=(0.5, 0.5, 0.5, 0.2)) 69 Fill(parent=self.mainPar, color=(0.0, 0.0, 0.0, 0.25)) 70 self.mainPar.GetMenu = self.GetHistoryBarMenu 71 self.scrollHandleCont = Container(name='scrollHandleCont', parent=self.mainPar, align=uiconst.CENTERLEFT, height=self.height) 72 self.bitParent = Container(name='bitParent', parent=self.mainPar, align=uiconst.CENTERLEFT, height=self.height - 6) 73 self.bits = [] 74 self.scrollHandle = Container(parent=self.scrollHandleCont, align=uiconst.TOPLEFT, pos=(0, 75 1, 76 self.bitWidth, 77 self.height - 2), color=(1.0, 1.0, 1.0, 1.0), state=uiconst.UI_NORMAL) 78 self.scrollHandle.cursor = uiconst.UICURSOR_POINTER_MENU 79 Fill(parent=self.scrollHandle, color=(1.0, 1.0, 1.0, 1.0)) 80 self.scrollHandle.OnMouseDown = self.SH_MouseDown 81 self.scrollHandle.OnMouseUp = self.SH_MouseUp 82 self.scrollHandle.GetMenu = self.GetScrollHandleMenu 83 84 def UpdateHistory(self, updateCode = None): 85 history = self.historyController.GetHistory() 86 if history is not None: 87 self.LoadHistory(len(history), updateCode) 88 89 def GetCurrentIndexAndMaxIndex(self): 90 lastLitIdx = self.historyController.lastLitIdx 91 if lastLitIdx and self.bits: 92 currentIndex = min(lastLitIdx, len(self.bits) - 1) 93 else: 94 currentIndex = 0 95 return (currentIndex, len(self.bits) - 1) 96 97 def OnButtonClicked(self, direction, *args): 98 PlaySound(uiconst.SOUND_BUTTON_CLICK) 99 if self.bitChangeCheck and not self.bitChangeCheck(): 100 return 101 self.ButtonScroll(direction) 102 103 def ButtonScroll(self, direction): 104 currentIndex, maxIndex = self.GetCurrentIndexAndMaxIndex() 105 if currentIndex + direction > maxIndex: 106 return 107 newIndex = max(0, min(len(self.bits), direction + currentIndex)) 108 self.historyController.lastLitIdx = newIndex 109 self.SettleScrollHandle(UPDATE_CODE_SELECTED) 110 self.UpdateBitsState() 111 self.UpdatePosition() 112 113 def LoadHistory(self, historyLength, updateCode = None): 114 self.bitParent.Flush() 115 self.bits = [] 116 litBit = None 117 widthForBit = self.bitWidth + self.bitGap 118 for i in xrange(historyLength): 119 bit = HistoryBit(bitIdx=i, parent=self.bitParent, name='bit_%s' % i, pos=(i * widthForBit, 120 0, 121 widthForBit, 122 self.bitHeight), callback=self.OnClickBit, bitGap=self.bitGap) 123 bit.GetMenu = (self.GetBitMenu, i) 124 bit.GetHint = lambda bitIdx = i: self.GetBitHint(bitIdx) 125 if i == self.historyController.lastLoadIdx: 126 litBit = bit 127 self.bits.append(bit) 128 129 self.historyController.lastLoadIdx = None 130 self.UpdateContentWidth() 131 if litBit is None: 132 initing = updateCode == UPDATE_CODE_INIT 133 self.ScrollTo(1.0, initing=initing) 134 else: 135 self.historyController.lastLitIdx = litBit.bitIdx 136 self.SettleScrollHandle(updateCode=updateCode) 137 self.UpdateBitsState() 138 self.UpdatePosition() 139 140 def OnClickBit(self, bit, *args): 141 PlaySound(uiconst.SOUND_BUTTON_CLICK) 142 if self.bitChangeCheck and not self.bitChangeCheck(): 143 return 144 self.historyController.lastLitIdx = bit.bitIdx 145 self.SettleScrollHandle(UPDATE_CODE_SELECTED) 146 self.UpdateBitsState() 147 148 def GetBitHint(self, bitIdx): 149 if self.bitHintFunc: 150 bitData = self.historyController.GetHistoryFromIdx(bitIdx) 151 return self.bitHintFunc(bitData) 152 153 def UpdateContentWidth(self): 154 widthForBit = self.bitWidth + self.bitGap 155 self.bitParent.width = len(self.bits) * widthForBit + self.bitGap 156 self.scrollHandleCont.width = self.bitParent.width 157 158 def ScrollTo(self, portion, initing = False): 159 l, t, w, h = self.mainPar.GetAbsolute() 160 self.scrollHandle.left = int((self.bitParent.width - self.scrollHandle.width) * portion) + self.bitGap 161 self.UpdatePosition() 162 updateCode = UPDATE_CODE_INIT if initing else None 163 self.SettleScrollHandle(updateCode) 164 165 def SH_MouseDown(self, *args): 166 if self.bitChangeCheck and not self.bitChangeCheck(): 167 return 168 l, t, w, h = self.scrollHandle.GetAbsolute() 169 self._mouseOffset = uicore.uilib.x - l 170 self._scrolling = True 171 self.scrollTimer = timerstuff.AutoTimer(5, self.DragScroll) 172 173 def SH_MouseUp(self, *args): 174 if self.bitChangeCheck and not self.bitChangeCheck(): 175 return 176 self._scrolling = False 177 self._mouseOffset = 0 178 self.scrollTimer = None 179 self.SettleScrollHandle(UPDATE_CODE_SELECTED) 180 181 def SettleScrollHandle(self, updateCode = None): 182 initing = updateCode == UPDATE_CODE_INIT 183 lastLitIdx = self.historyController.lastLitIdx 184 if lastLitIdx is not None: 185 lastLit = self.bits[lastLitIdx] 186 if not initing: 187 if updateCode == UPDATE_CODE_SELECTED and self.callback and self.isAllowedToLoadFunc: 188 currentBitData = self.historyController.GetHistoryFromIdx(self.historyController.currentIndex) 189 toBeLoaded = self.historyController.GetHistoryFromIdx(lastLitIdx) 190 if not self.isAllowedToLoadFunc(currentBitData, toBeLoaded): 191 return 192 self.historyController.currentIndex = lastLitIdx 193 self.historyController.lastLoadIdx = lastLitIdx 194 if self.callback: 195 historyBitData = self.historyController.GetHistoryFromLastLit() 196 uthread.new(self.callback, historyBitData, updateCode) 197 self.scrollHandle.left = lastLit.left + self.bitGap 198 else: 199 self.scrollHandle.left = -2 200 self.leftBtn.Enable() 201 self.rightBtn.Enable() 202 self.scrollHandle.Show() 203 if self.historyController.IsOnFirstBit(): 204 self.leftBtn.Disable() 205 self.leftBtn.UpdateIconState() 206 if self.historyController.IsOnLastBit(): 207 self.rightBtn.Disable() 208 self.rightBtn.UpdateIconState() 209 if self.historyController.lastLitIdx is None: 210 self.leftBtn.Disable() 211 self.rightBtn.Disable() 212 self.scrollHandle.Hide() 213 214 def DragScroll(self, *args): 215 l, t, w, h = self.mainPar.GetAbsolute() 216 bl, bt, bw, bh = self.bitParent.GetAbsolute() 217 self.scrollHandle.left = min(bw - self.scrollHandle.width, max(0, uicore.uilib.x - self._mouseOffset - bl)) 218 self.UpdatePosition() 219 220 def UpdatePosition(self): 221 l, t, w, h = self.mainPar.GetAbsolute() 222 sl, st, sw, sh = self.scrollHandle.GetAbsolute() 223 if self.bitParent.width <= w: 224 self.bitParent.left = 0 225 elif sl < l: 226 self.bitParent.left += l - sl 227 elif sl + sw > l + w: 228 self.bitParent.left -= sl + sw - (l + w) 229 self.scrollHandleCont.left = self.bitParent.left 230 self.UpdateBitsState() 231 232 def UpdateBitsState(self): 233 self.historyController.lastLitIdx = None 234 l, t, w, h = self.bitParent.GetAbsolute() 235 for bit in self.bits: 236 bitVale = bit.left + self.bitGap + self.bitWidth / 2 237 handleValue = self.scrollHandle.left + self.scrollHandle.width / 2 238 if bitVale > handleValue: 239 bit.SetFaded() 240 else: 241 bit.SetNormal() 242 self.historyController.lastLitIdx = bit.bitIdx 243 244 def OnCorrectPosition(self, currentHistoryData): 245 lastLoaded = self.historyController.GetHistoryFromLastLit() 246 if currentHistoryData != lastLoaded: 247 self.historyController.lastLitIdx = None 248 self.SettleScrollHandle(UPDATE_CODE_CORRECTION) 249 250 def GetScrollHandleMenu(self): 251 if self.historyController.lastLitIdx is None: 252 return [] 253 m = self.GetBitMenu(self.historyController.lastLitIdx) 254 return m 255 256 def GetBitMenu(self, bitIdx, *args): 257 m = [(MenuLabel('UI/Commands/Remove'), self.historyController.RemoveFromHistory, (bitIdx,))] 258 m += self.GetHistoryBarMenu() 259 return m 260 261 def GetHistoryBarMenu(self): 262 return [(MenuLabel('/Carbon/UI/Controls/Common/ClearHistory'), self.ClearHistory)] 263 264 def ClearHistory(self): 265 self.historyController.ClearHistory() 266 267 def Close(self): 268 with ExceptionEater('closingHistoryBar'): 269 self.ChangeSignalConnection(connect=False) 270 Container.Close(self) 271 272 273 class HistoryBit(Container): 274 default_state = uiconst.UI_NORMAL 275 default_align = uiconst.TOPLEFT 276 default_bitGap = 0 277 278 def ApplyAttributes(self, attributes): 279 Container.ApplyAttributes(self, attributes) 280 self.bitIdx = attributes.get('bitIdx') 281 self.idx = self.bitIdx 282 bitGap = attributes.get('bitGap', self.default_bitGap) 283 self.callback = attributes.get('callback', None) 284 fillColor = (1.0, 1.0, 1.0, 1.0) 285 self.bitFill = Fill(name='bitFill', parent=self, color=fillColor, padding=(bitGap, 286 0, 287 0, 288 0)) 289 290 def GetNomalOpacity(self): 291 if self.bitIdx % 2: 292 return 0.7 293 return 0.5 294 295 def SetFaded(self): 296 self.bitFill.opacity = 0.25 297 298 def SetNormal(self): 299 self.bitFill.opacity = self.GetNomalOpacity() 300 301 def OnClick(self, *args): 302 if self.callback: 303 self.callback(self) 304 305 def OnMouseEnter(self): 306 PlaySound(uiconst.SOUND_BUTTON_HOVER) 307 self.bitFill.padTop = -2 308 self.bitFill.padBottom = -2 309 310 def OnMouseExit(self, *args): 311 self.bitFill.padTop = 0 312 self.bitFill.padBottom = 0 313 314 315 class HistoryController(object): 316 317 def __init__(self, history = None, overwriteHistory = True): 318 self.overwriteHistory = overwriteHistory 319 self.ResetVariables() 320 if history: 321 self.history = history[:] 322 self.on_history_updated = Signal(signalName='on_history_updated') 323 self.on_history_correction_triggered = Signal(signalName='on_history_correction_triggered') 324 325 def ResetVariables(self): 326 self._currentIndex = None 327 self._lastLoadIndex = None 328 self._lastLitIdx = None 329 self.history = [] 330 331 @property 332 def currentIndex(self): 333 return self._currentIndex 334 335 @currentIndex.setter 336 def currentIndex(self, value): 337 self._currentIndex = value 338 339 @property 340 def lastLoadIdx(self): 341 return self._lastLoadIndex 342 343 @lastLoadIdx.setter 344 def lastLoadIdx(self, value): 345 self._lastLoadIndex = value 346 347 @property 348 def lastLitIdx(self): 349 return self._lastLitIdx 350 351 @lastLitIdx.setter 352 def lastLitIdx(self, value): 353 self._lastLitIdx = value 354 355 def LoadHistory(self, history): 356 self.history = history 357 358 def AddToHistory(self, value, sendSignal = True): 359 if self.overwriteHistory: 360 if self.lastLitIdx: 361 self.history = self.history[:self.lastLitIdx + 1] 362 self.history.append(value) 363 self.lastLoadIdx = len(self.history) - 1 364 if sendSignal: 365 self.on_history_updated(UPDATE_CODE_ADD) 366 367 def RemoveFromHistory(self, idx): 368 if idx <= len(self.history) - 1: 369 self.history.pop(idx) 370 if self.lastLitIdx > idx: 371 self.lastLitIdx = max(0, self.lastLitIdx - 1) 372 if self.lastLoadIdx > idx: 373 self.lastLoadIdx = max(0, self.lastLoadIdx - 1) 374 if self.currentIndex > idx: 375 self.currentIndex = max(0, self.currentIndex - 1) 376 self.on_history_updated(UPDATE_CODE_REMOVE) 377 378 def ClearHistory(self, sendSignal = True): 379 self.ResetVariables() 380 if sendSignal: 381 self.on_history_updated(UPDATE_CODE_CLEAR) 382 383 def GetHistory(self): 384 return self.history 385 386 def GetHistoryFromLastLit(self): 387 return self.GetHistoryFromIdx(self.lastLitIdx) 388 389 def GetHistoryFromIdx(self, idx): 390 historyIdx = max(0, min(len(self.history) - 1, idx)) 391 if historyIdx <= len(self.history) - 1: 392 return self.history[historyIdx] 393 394 def IsOnFirstBit(self): 395 return self.lastLitIdx == 0 396 397 def IsOnLastBit(self): 398 return self.lastLitIdx == len(self.history) - 1 399 400 def TriggerHistoryCorrection(self, historyData): 401 self.on_history_correction_triggered(historyData)