slider.py (17833B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\carbonui\control\slider.py 2 import uthread2 3 from carbon.client.script.environment.AudioUtil import PlaySound 4 from carbon.common.script.util.mathCommon import FloatCloseEnough 5 from carbonui.primitives.fill import Fill 6 from carbonui.primitives.frame import Frame 7 from carbonui.uianimations import animations 8 from eve.client.script.ui import eveColor, eveThemeColor 9 from eve.client.script.ui.control.eveLabel import EveLabelMedium 10 from eve.client.script.ui.control.themeColored import SpriteThemeColored, FrameThemeColored 11 import carbonui.const as uiconst 12 from carbonui.primitives.container import Container 13 from carbonui.primitives.line import Line 14 from carbonui.util.various_unsorted import GetAttrs 15 from carbonui.uicore import uicore 16 from signals import Signal 17 MOUSE_WHEEL_SPEED = 0.0005 18 BAR_PADDING = 5 19 20 class Slider(Container): 21 default_align = uiconst.TOTOP 22 default_height = 16 23 default_barHeight = 6 24 default_handleSize = 12 25 default_minValue = 0.0 26 default_maxValue = 1.0 27 default_value = None 28 default_config = '' 29 default_setting = None 30 default_label = None 31 default_minLabel = None 32 default_maxLabel = None 33 default_increments = None 34 default_snapToIncrements = True 35 default_isEvenIncrements = False 36 default_getHintFunc = None 37 default_mouseWheelEnabled = True 38 default_isInteger = False 39 default_callback = None 40 default_on_dragging = None 41 42 def ApplyAttributes(self, attributes): 43 super(Slider, self).ApplyAttributes(attributes) 44 self.value = attributes.get('value', self.default_value) 45 self.maxValue = attributes.get('maxValue', self.default_maxValue) 46 self.minValue = attributes.get('minValue', self.default_minValue) 47 self.handle_size = attributes.get('handleSize', self.default_handleSize) 48 self._labelText = attributes.get('label', self.default_label) 49 self._minLabelText = attributes.get('minLabel', self.default_minLabel) 50 self._maxLabelText = attributes.get('maxLabel', self.default_maxLabel) 51 self._mouseWheelEnabled = attributes.get('mouseWheelEnabled', self.default_mouseWheelEnabled) 52 self.barHeight = attributes.get('barHeight', self.default_barHeight) 53 self.isInteger = attributes.get('isInteger', self.default_isInteger) 54 self.incrementValues = attributes.get('increments', self.default_increments) or [] 55 self.snapToIncrements = attributes.get('snapToIncrements', self.default_snapToIncrements) 56 self.isEvenIncrementsSlider = attributes.get('isEvenIncrementsSlider', self.default_isEvenIncrements) and bool(self.incrementValues) 57 self.config = attributes.get('config', self.default_config) 58 self.setting = attributes.get('setting', self.default_setting) 59 if self.setting: 60 self.minValue = self.setting.min_value 61 self.maxValue = self.setting.max_value 62 self._getHintFunc = attributes.get('getHintFunc', self.default_getHintFunc) 63 callback_func = attributes.get('callback', self.default_callback) 64 on_dragging_func = attributes.get('on_dragging', self.default_on_dragging) 65 self.isDragging = False 66 self._hasMouseMovedOver = False 67 self.handle = None 68 self.handleOffset = 0 69 self.incrementValuesNormalized = [] 70 self.tickLines = [] 71 self._ConstructBarCont() 72 self._ConstructHandle() 73 self.ticksCont = Container(name='ticksCont', parent=self.barCont) 74 self._ConstructBackground() 75 self._ConstructLabel() 76 self._ConstructMinMaxLabels() 77 self.SetIncrements(self.incrementValues) 78 self._InitializeValue() 79 self._ConstructSignals(callback_func, on_dragging_func) 80 81 def _ConstructMinMaxLabels(self): 82 if not (self._minLabelText or self._maxLabelText): 83 return 84 self.maxLabel = EveLabelMedium(parent=self, align=uiconst.BOTTOMLEFT, text=self._minLabelText) 85 self.minLabel = EveLabelMedium(parent=self, align=uiconst.BOTTOMRIGHT, text=self._maxLabelText) 86 _, textHeight = EveLabelMedium.MeasureTextSize(self._minLabelText or '' + self._maxLabelText or '') 87 self.height += textHeight 88 89 def _ConstructSignals(self, callback_func, on_dragging_func): 90 self.callback = Signal('Slider.callback') 91 if callback_func: 92 self.callback.connect(callback_func) 93 self.on_dragging = Signal('Slider.on_dragging') 94 if on_dragging_func: 95 self.on_dragging.connect(on_dragging_func) 96 97 def _InitializeValue(self): 98 if self.setting: 99 value = self.setting.get() 100 elif self.config: 101 value = self._GetValueFromSetting() 102 elif self.value is not None: 103 value = self.value 104 else: 105 value = self.minValue 106 self.SetValue(value) 107 108 def _GetValueFromSetting(self): 109 if len(self.config) == 3: 110 cfgName, prefsType, defaultValue = self.config 111 if prefsType is not None: 112 si = GetAttrs(settings, *prefsType) 113 if si is not None: 114 value = si.Get(cfgName, defaultValue) 115 else: 116 value = defaultValue 117 else: 118 value = defaultValue 119 else: 120 value = settings.user.ui.Get(self.config, (self.maxValue - self.minValue) * 0.5) 121 if value is None: 122 value = 0.0 123 return value 124 125 def _ConstructBarCont(self): 126 self.barCont = Container(parent=self, name='barCont', align=uiconst.TOTOP, state=uiconst.UI_NORMAL, height=self.barHeight + 2 * BAR_PADDING) 127 self.barCont.OnClick = self.OnSliderClicked 128 self.barCont.OnMouseMove = self.OnSliderMouseMove 129 self.barCont.OnMouseExit = self.OnMouseBarExit 130 self.barCont.OnMouseWheel = self.OnMouseWheel 131 self.barCont.GetHint = self.GetHint 132 self.barCont.GetTooltipPosition = self.GetTooltipPosition 133 134 def _ConstructBackground(self): 135 bgCont = Container(name='bgCont', padding=(0, 136 BAR_PADDING, 137 0, 138 BAR_PADDING), parent=self.barCont) 139 Frame(bgParent=bgCont, name='bgFrame', texturePath='res:/UI/Texture/Classes/Slider/bgFrame.png', color=eveColor.MATTE_BLACK, cornerSize=3) 140 Frame(bgParent=bgCont, name='bgFrame', texturePath='res:/UI/Texture/Classes/Slider/bgFill.png', color=eveColor.BLACK, opacity=0.6, cornerSize=3) 141 142 def OnSliderClicked(self, *args): 143 _, newValue = self._FindNewValue() 144 self.SetValue(newValue) 145 self._OnEndUserAction() 146 147 def _OnEndUserAction(self): 148 if self.setting: 149 self.setting.set(self.GetValue()) 150 self._ApplyConfigSetting() 151 self.callback(self) 152 153 def OnSliderMouseMove(self, *args): 154 self._hasMouseMovedOver = True 155 if not self.incrementValues or not self.snapToIncrements: 156 return 157 _, value = self._FindNewValue() 158 if value not in self.incrementValues or not self.tickLines: 159 return 160 valueIndex = self.incrementValues.index(value) 161 self._ApplyTickOpacity(valueIndex) 162 163 def _ApplyTickOpacity(self, valueIndex = None): 164 for i, tick in enumerate(self.tickLines): 165 isSelected = i == valueIndex 166 tick.opacity = self._GetTickOpacity(i, isSelected) 167 168 def _GetTickOpacity(self, i, isSelected = False): 169 if isSelected: 170 return 1.5 171 elif self.incrementValuesNormalized[i] in (0.0, 1.0): 172 return 0.0 173 else: 174 return 1.0 175 176 def _ConstructHandle(self): 177 self.handleOffset = 4 178 self.handle = Container(name='handle', parent=self.barCont, state=uiconst.UI_NORMAL, align=uiconst.CENTERLEFT, pos=(0, 179 0, 180 self.handle_size, 181 self.handle_size)) 182 self.handleFill = Fill(name='handleFill', parent=self.handle, align=uiconst.CENTER, pos=(0, 0, 4, 8), outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.0, color=eveThemeColor.THEME_FOCUS) 183 self.handle.OnMouseEnter = self.OnHandleMouseEnter 184 self.handle.OnMouseDown = self.OnHandleMouseDown 185 self.handle.OnMouseUp = self.OnHandleMouseUp 186 self.handle.OnMouseMove = self.OnHandleMouseMove 187 self.handle.GetHint = self.GetHint 188 189 def _ConstructLabel(self): 190 if self._labelText: 191 self.label = EveLabelMedium(parent=self, top=13, state=uiconst.UI_NORMAL, hint=self.hint, text=self._labelText) 192 else: 193 self.label = None 194 195 def ReconstructIncrements(self): 196 for tick in self.tickLines: 197 tick.Close() 198 199 self.tickLines = [] 200 if self.incrementValues: 201 w, _ = self.GetAbsoluteSize() 202 maxX = self._GetMaxX() 203 for i, valueNormalized in enumerate(self.incrementValuesNormalized): 204 left = valueNormalized 205 line = self._ConstructTickLine(i, left) 206 self.tickLines.append(line) 207 208 def _ConstructTickLine(self, i, left): 209 padding = BAR_PADDING + 2 210 return Line(parent=self.ticksCont, name='tickLine', align=uiconst.TOPLEFT_PROP, color=eveColor.SMOKE_BLUE, opacity=self._GetTickOpacity(i), left=left, width=1, top=7, height=self.barHeight - 4) 211 212 def SetIncrements(self, values): 213 self.incrementValues = values 214 numIncrements = len(self.incrementValues) 215 self.incrementValuesNormalized = [] 216 for idx, value in enumerate(self.incrementValues): 217 if self.isEvenIncrementsSlider: 218 normalizedValue = float(idx) / (numIncrements - 1) 219 else: 220 normalizedValue = (value - self.minValue) / float(self.maxValue - self.minValue) 221 self.incrementValuesNormalized.append(normalizedValue) 222 223 self.ReconstructIncrements() 224 225 def GetValue(self): 226 return self.value 227 228 def _UpdateHandlePosition(self, nValue, useIncrements = True): 229 maxX = self._GetMaxX() 230 leftPercentage = max(0, nValue) 231 if self.incrementValues and useIncrements and self.snapToIncrements: 232 leftPercentage = self._FindClosest(leftPercentage, self.incrementValuesNormalized) 233 left = leftPercentage * maxX 234 self.handle.left = int(left) - self.handleOffset 235 236 def SetValue(self, value): 237 self.value = self._GetSanitizedValue(value, useIncrements=False) 238 self._UpdateHandlePosition(self.GetNormalizedValue(), useIncrements=False) 239 240 def _GetSanitizedValue(self, value, useIncrements = False): 241 if self.incrementValues and useIncrements and self.snapToIncrements: 242 value = self._FindClosest(round(value, 2), self.incrementValues) 243 value = max(self.minValue, min(self.maxValue, value)) 244 if self.isInteger: 245 value = round(value) 246 return value 247 248 def GetNormalizedValue(self): 249 if self.isEvenIncrementsSlider: 250 valueIndex = self.incrementValues.index(self.value) 251 percentage = self.incrementValuesNormalized[valueIndex] 252 return percentage 253 else: 254 return float(self.value - self.minValue) / (-self.minValue + self.maxValue) 255 256 def _FindClosest(self, check, values): 257 mindiff = values[-1] - values[0] 258 found = mindiff 259 for value in values: 260 diff = abs(value - check) 261 if diff < mindiff: 262 mindiff = diff 263 found = value 264 265 return found 266 267 def OnColorThemeChanged(self): 268 super(Slider, self).OnColorThemeChanged() 269 self.handleFill.SetRGBA(*eveThemeColor.THEME_FOCUS) 270 271 def OnMouseWheel(self, delta, *args): 272 if not self._hasMouseMovedOver or not self._mouseWheelEnabled: 273 return False 274 if self.incrementValues and self.snapToIncrements: 275 value = self._GetMouseWheelNewValueIncrements(delta) 276 else: 277 value = self._GetMouseWheelNewValue(delta) 278 if value is not None: 279 self.SetValue(value) 280 self._OnEndUserAction() 281 282 def _GetMouseWheelNewValue(self, delta): 283 change = MOUSE_WHEEL_SPEED * delta * (self.maxValue - self.minValue) 284 newValue = self._GetSanitizedValue(self.GetValue() + change) 285 return newValue 286 287 def _GetMouseWheelNewValueIncrements(self, delta): 288 currentValue = self.GetValue() 289 if delta < 0: 290 if currentValue <= self.incrementValues[0]: 291 newValue = None 292 else: 293 newValue = max((x for x in self.incrementValues if x < currentValue and not FloatCloseEnough(x, currentValue))) 294 elif currentValue >= self.incrementValues[-1]: 295 newValue = None 296 else: 297 newValue = min((x for x in self.incrementValues if x > currentValue and not FloatCloseEnough(x, currentValue))) 298 return newValue 299 300 def OnMouseBarExit(self): 301 self._ApplyTickOpacity(-1) 302 303 def OnMouseEnter(self, *args): 304 animations.MorphScalar(self.handleFill, 'glowBrightness', startVal=self.handleFill.glowBrightness, endVal=1.0, duration=0.1) 305 306 def OnMouseExit(self, *args): 307 self._hasMouseMovedOver = False 308 animations.MorphScalar(self.handleFill, 'glowBrightness', startVal=self.handleFill.glowBrightness, endVal=0.0, duration=0.5) 309 310 def OnHandleMouseEnter(self, *args): 311 PlaySound(uiconst.SOUND_BUTTON_HOVER) 312 313 def OnHandleMouseDown(self, *args): 314 self.isDragging = True 315 self._ApplyTickOpacity(-1) 316 uthread2.StartTasklet(self._TooltipUpdateThread) 317 animations.SpColorMorphTo(self.handleFill, self.handleFill.GetRGBA(), eveColor.WHITE, duration=0.1) 318 animations.MorphScalar(self.handleFill, 'glowBrightness', self.handleFill.glowBrightness, 0.3, duration=0.1) 319 320 def _TooltipUpdateThread(self): 321 while self.isDragging: 322 uicore.uilib.tooltipHandler.UpdateTooltipHint(self) 323 uthread2.Yield() 324 325 def OnHandleMouseUp(self, *args): 326 PlaySound(uiconst.SOUND_BUTTON_CLICK) 327 uicore.uilib.UnclipCursor() 328 self.isDragging = False 329 self._OnEndUserAction() 330 animations.SpColorMorphTo(self.handleFill, self.handleFill.GetRGBA(), eveThemeColor.THEME_FOCUS, duration=0.3) 331 animations.MorphScalar(self.handleFill, 'glowBrightness', self.handleFill.glowBrightness, 1.0, duration=0.3, curveType=uiconst.ANIM_OVERSHOT5) 332 333 def _ApplyConfigSetting(self): 334 if self.config: 335 if len(self.config) == 3: 336 cfgName, prefsType, defaultValue = self.config 337 if prefsType: 338 si = GetAttrs(settings, *prefsType) 339 if si is not None: 340 value = si.Set(cfgName, self.value) 341 else: 342 settings.user.ui.Set(self.config, self.value) 343 344 def OnHandleMouseMove(self, *args): 345 if self.isDragging: 346 PlaySound(uiconst.SOUND_VALUECHANGE_TICK) 347 localMousePos, value = self._FindNewValue() 348 if self.handle.left + self.handleOffset == localMousePos: 349 return 350 self.handle.left = localMousePos - self.handleOffset 351 self.value = self._GetSanitizedValue(value, useIncrements=True) 352 self._UpdateHandlePosition(self.GetNormalizedValue(), useIncrements=True) 353 self.on_dragging(self) 354 355 def _FindNewValue(self): 356 l, _, w, _ = self.GetAbsolute() 357 hw = self._GetHandleWidth() 358 maxX = self._GetMaxX() 359 localMousePos = uicore.uilib.x - l - hw / 2 360 localMousePos = max(0, min(maxX, localMousePos)) 361 localMousePercentagePos = min(1, max(0, float(localMousePos) / maxX)) 362 if self.isEvenIncrementsSlider: 363 numIncrements = len(self.incrementValuesNormalized) 364 eachStep = float(maxX) / (numIncrements - 1) 365 localMousePercentagePos = self._FindClosest(localMousePercentagePos, self.incrementValuesNormalized) 366 partIndex = self.incrementValuesNormalized.index(localMousePercentagePos) 367 localMousePos = partIndex * eachStep 368 value = self.incrementValues[partIndex] 369 else: 370 if self.incrementValues and self.snapToIncrements: 371 localMousePercentagePos = self._FindClosest(localMousePercentagePos, self.incrementValuesNormalized) 372 localMousePos = localMousePercentagePos * maxX 373 value = self.minValue + localMousePercentagePos * (self.maxValue - self.minValue) 374 return (localMousePos, value) 375 376 def _GetMaxX(self): 377 w, _ = self.GetAbsoluteSize() 378 maxX = w - self._GetHandleWidth() 379 return maxX 380 381 def _GetHandleWidth(self): 382 return self.handle.width - 2 * self.handleOffset 383 384 def Enable(self, *args): 385 self.SetState(uiconst.UI_NORMAL) 386 animations.FadeIn(self) 387 388 def Disable(self, *args): 389 self.SetState(uiconst.UI_DISABLED) 390 animations.FadeTo(self, startVal=self.opacity, endVal=0.5) 391 392 def GetHint(self): 393 if self._getHintFunc: 394 return self._getHintFunc(self) 395 else: 396 return super(Slider, self).GetHint() 397 398 def GetTooltipPosition(self): 399 return self.handle.GetAbsolute() 400 401 def SetLabel(self, text): 402 self._labelText = text 403 if not self.label: 404 self._ConstructLabel() 405 else: 406 self.label.SetText(text) 407 408 def UpdateAlignment(self, budgetLeft = 0, budgetTop = 0, budgetWidth = 0, budgetHeight = 0, updateChildrenOnly = False): 409 ret = super(Slider, self).UpdateAlignment(budgetLeft, budgetTop, budgetWidth, budgetHeight, updateChildrenOnly) 410 if self.value is not None: 411 self._UpdateHandlePosition(self.GetNormalizedValue()) 412 return ret