toggleSwitch.py (16020B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\control\toggleSwitch.py 2 import math 3 import carbonui 4 import carbonui.text.styles 5 import threadutils 6 import trinity 7 from carbon.client.script.environment.AudioUtil import PlaySound 8 from carbonui import PickState 9 from carbonui.control.button import BusyContext 10 from carbonui.primitives.container import Container 11 from carbonui.primitives.frame import Frame 12 from carbonui.primitives.sprite import Sprite 13 from carbonui.primitives.stretchspritehorizontal import StretchSpriteHorizontal 14 from carbonui.primitives.transform import Transform 15 from carbonui.uianimations import animations 16 from carbonui.util.various_unsorted import IsUnder 17 from eve.client.script.ui import eveColor, eveThemeColor 18 from localization import GetByLabel 19 import carbonui.const as uiconst 20 from signals import Signal 21 from carbonui.uicore import uicore 22 ON_FRAME_OPACITY = 0.6 23 24 class BaseToggleSwitch(Container): 25 default_width = 24 26 default_height = 16 27 default_minWidth = 24 28 default_minHeight = 24 29 default_enabled = True 30 default_align = carbonui.Align.TOPLEFT 31 default_state = carbonui.uiconst.UI_NORMAL 32 default_clipChildren = True 33 default_checked = False 34 default_setting = None 35 default_onText = GetByLabel('UI/SystemMenu/DisplayAndGraphics/Common/On') 36 default_offText = GetByLabel('UI/SystemMenu/DisplayAndGraphics/Common/Off') 37 default_handleSize = 12 38 handleLabelOffset = 4 39 labelVerticalPadding = 2 40 handleTexturePath = 'res:/UI/Texture/classes/toggleSwitch/handle.png' 41 handleTextureChecked = handleTexturePath 42 __busy_context = None 43 44 def ApplyAttributes(self, attributes): 45 self.onFrame = None 46 self.offFrame = None 47 super(BaseToggleSwitch, self).ApplyAttributes(attributes) 48 self.onText = attributes.get('onText', self.default_onText) 49 self.offText = attributes.get('offText', self.default_offText) 50 handleSize = attributes.get('handleSize', self.default_handleSize) 51 setting = attributes.get('setting', self.default_setting) 52 enabled = attributes.get('enabled', self.default_enabled) 53 self.confirmFunc = attributes.get('confirmFunc', None) 54 self.switchController = SwitchController(setting, enabled, handleSize) 55 self.switchController.on_set_checked.connect(self.SetChecked) 56 self.switchController.on_busy_changed.connect(self.UpdateBusy) 57 self.on_switch_changed = Signal(signalName='on_switch_changed') 58 isChecked = attributes.get('checked', self.default_checked) 59 self.isTabStop = 1 60 self.ConstructLayout() 61 if self.switchController.HasSettings(): 62 isChecked = self.switchController.GetIsCheckedSettingValue() 63 self.SetChecked(isChecked, 0) 64 self.UpdateBusy() 65 66 def ConstructLayout(self): 67 self.ConstructSliderHandle() 68 self._ConstructFrame() 69 70 def ConstructSliderHandle(self): 71 handleSize = self.switchController.handleSize 72 self.sliderHandle = Container(name='sliderHandle', parent=self, fillCenter=True, align=carbonui.Align.CENTERLEFT, pos=(self.handleLabelOffset, 73 0, 74 handleSize, 75 handleSize)) 76 self.sliderHandleSprite = Sprite(parent=self.sliderHandle, pos=(0, 77 0, 78 handleSize, 79 handleSize), align=carbonui.Align.CENTER, texturePath=self.handleTexturePath, state=uiconst.UI_DISABLED) 80 81 def _ConstructFrame(self): 82 pass 83 84 def SetChecked(self, isChecked, report = 1): 85 isChecked = isChecked or 0 86 self.switchController.ForceSetCheckedValue(int(isChecked)) 87 self.SetCheckLook(bool(report)) 88 if report: 89 if isChecked: 90 PlaySound(uiconst.SOUND_SETDESELECTED) 91 else: 92 PlaySound(uiconst.SOUND_SETSELECTED) 93 self.switchController.UpdateSettings() 94 self.on_switch_changed(self, self.switchController.checked) 95 96 def SetCheckLook(self, animate = False): 97 isChecked = self.switchController.checked 98 self.sliderHandle.StopAnimations() 99 if isChecked: 100 newLeft = self.width - self.switchController.handleSize - self.handleLabelOffset 101 handleTexturePath = self.handleTextureChecked 102 handleOpacity = 1.0 103 else: 104 newLeft = self.handleLabelOffset 105 handleTexturePath = self.handleTexturePath 106 handleOpacity = 0.75 107 self.sliderHandleSprite.texturePath = handleTexturePath 108 self.sliderHandleSprite.opacity = handleOpacity 109 if animate: 110 animations.MorphScalar(self.sliderHandle, 'left', startVal=self.sliderHandle.left, endVal=newLeft, duration=0.2) 111 else: 112 self.sliderHandle.left = newLeft 113 self.onFrame.display = bool(isChecked) 114 self.offFrame.display = not bool(isChecked) 115 116 def SetValue(self, value, report = True): 117 if report: 118 self.checked = value 119 else: 120 self.switchController.ForceSetCheckedValue(value) 121 122 def OnClick(self, *args): 123 if not self.switchController.disabled: 124 if self.WasConfirmed(): 125 self.ToggleState() 126 127 def WasConfirmed(self): 128 if self.confirmFunc: 129 return self.confirmFunc(self.checked) 130 return True 131 132 def ToggleState(self): 133 self.SetChecked(not self.switchController.checked) 134 135 def OnMouseEnter(self, *args): 136 self.switchController.hovered = True 137 if self.switchController.disabled: 138 return 139 self.UpdateOpacity() 140 141 def OnMouseExit(self, *args): 142 self.switchController.hovered = False 143 if self.switchController.disabled: 144 return 145 self.UpdateOpacity() 146 147 def UpdateOpacity(self): 148 onFrameOpacity, offFrameOpacity = self._GetOpacityValues() 149 self.onFrame.opacity = onFrameOpacity 150 self.offFrame.opacity = offFrameOpacity 151 152 def _GetOpacityValues(self): 153 mouseOver = uicore.uilib.mouseOver 154 overSwitch = mouseOver and (mouseOver == self or mouseOver.IsUnder(self)) 155 if overSwitch: 156 return self._GetMouseOverOpacity() 157 else: 158 return self._GetIdleOpacity() 159 160 def _GetMouseOverOpacity(self): 161 return (0.9, 1.5) 162 163 def _GetIdleOpacity(self): 164 return (ON_FRAME_OPACITY, 1.0) 165 166 def GetHint(self): 167 hintList = [] 168 if self.switchController.checked: 169 onlineStatus = self.onText 170 else: 171 onlineStatus = self.offText 172 hintList.append(GetByLabel('UI/Control/ToggleSwitchCurrentOnlineStatus', onlineStatus=onlineStatus)) 173 if not self.switchController.disabled: 174 hintList.append(GetByLabel('UI/Control/ToggleSwitchClickToToggle')) 175 return '<br>'.join(hintList) 176 177 def OnColorThemeChanged(self): 178 super(BaseToggleSwitch, self).OnColorThemeChanged() 179 self.onFrame.SetRGBA(*(eveThemeColor.THEME_FOCUS[:3] + (self.onFrame.opacity,))) 180 181 @property 182 def checked(self): 183 return self.switchController.checked 184 185 @checked.setter 186 def checked(self, value): 187 self.switchController.checked = value 188 189 @property 190 def busy(self): 191 return self.switchController.busy 192 193 @busy.setter 194 def busy(self, value): 195 self.switchController.busy = value 196 197 @property 198 def busy_context(self): 199 if self.__busy_context is None: 200 self.__busy_context = BusyContext(self) 201 return self.__busy_context 202 203 def UpdateBusy(self): 204 if self.destroyed: 205 return 206 if self.switchController.busy: 207 self.Disable() 208 else: 209 self.Enable() 210 211 212 class ToggleSwitch(BaseToggleSwitch): 213 default_adjustSize = True 214 _arrowsCont = None 215 handleTextureChecked = 'res:/UI/Texture/classes/toggleSwitch/handleCheckedSmall.png' 216 217 def ApplyAttributes(self, attributes): 218 super(ToggleSwitch, self).ApplyAttributes(attributes) 219 adjustSize = attributes.get('adjustSize', self.default_adjustSize) 220 if adjustSize: 221 self.AdjustSize(attributes) 222 223 def AdjustSize(self, attributes): 224 textwidthOn, textheightOn = self.onLabel.MeasureTextSize(self.onText) 225 textwidthOff, textheightOff = self.offLabel.MeasureTextSize(self.offText) 226 maxTextheight = max(textheightOn, textheightOff) 227 self.switchController.handleSize = max(self.switchController.handleSize, maxTextheight - 4) 228 maxLabelWidth = max(textwidthOn, textwidthOff) 229 self.width = max(self.width, maxLabelWidth + self.switchController.handleSize + 2.5 * self.handleLabelOffset) 230 self.height = max(self.height, maxTextheight + 2 * self.labelVerticalPadding) 231 self.UpdateHandleSizes() 232 self.SetCheckLook() 233 234 def UpdateHandleSizes(self): 235 handleSize = self.switchController.handleSize 236 for element in (self.sliderHandle, self.sliderHandleSprite): 237 element.width = handleSize 238 element.height = handleSize 239 240 def ConstructLayout(self): 241 super(ToggleSwitch, self).ConstructLayout() 242 self.ConstructLabels() 243 244 def ConstructLabels(self): 245 self.onLabel = carbonui.text.styles.TextDetail(parent=self, text=self.onText, align=carbonui.Align.CENTERLEFT, left=self.handleLabelOffset) 246 self.offLabel = carbonui.text.styles.TextDetail(parent=self, text=self.offText, align=carbonui.Align.CENTERRIGHT, left=self.handleLabelOffset) 247 248 def _ConstructFrame(self): 249 self.onFrame = Frame(name='onFrame', bgParent=self, frameConst=('res:/UI/Texture/classes/toggleSwitch/backgroundFill.png', 8, 0), opacity=ON_FRAME_OPACITY, color=eveThemeColor.THEME_FOCUSDARK) 250 self.offFrame = Frame(name='offFrame', bgParent=self, frameConst=('res:/UI/Texture/classes/toggleSwitch/backgroundFrame.png', 8, 0), color=eveThemeColor.THEME_FOCUSDARK) 251 252 def SetCheckLook(self, animate = False): 253 super(ToggleSwitch, self).SetCheckLook(animate) 254 self.onLabel.display = bool(self.switchController.checked) 255 self.offLabel.display = not bool(self.switchController.checked) 256 257 def OnColorThemeChanged(self): 258 super(ToggleSwitch, self).OnColorThemeChanged() 259 self.offFrame.SetRGBA(*(eveThemeColor.THEME_FOCUS[:3] + (self.offFrame.opacity,))) 260 261 def UpdateBusy(self): 262 if self.destroyed: 263 return 264 if self.switchController.busy: 265 self.PrepareBusyArrows() 266 self._arrowsCont._start_arrow_animation() 267 self.Disable() 268 else: 269 if self._arrowsCont is not None: 270 self._arrowsCont._stop_arrow_animation() 271 self.Enable() 272 273 def PrepareBusyArrows(self): 274 if self._arrowsCont is None: 275 self._arrowsCont = ArrowCont(parent=self, switchController=self.switchController) 276 return self._arrowsCont 277 278 279 class ToggleSwitchCompact(BaseToggleSwitch): 280 default_handleSize = 8 281 handleLabelOffset = 0 282 default_width = 16 283 handleTexturePath = 'res:/UI/Texture/classes/toggleSwitch/handleSmall.png' 284 285 def _ConstructFrame(self): 286 cont = Container(name='cont', parent=self, align=uiconst.VERTICALLY_CENTERED, height=4, opacity=0.8) 287 self.onFrame = StretchSpriteHorizontal(name='onFrame', parent=cont, align=uiconst.TOTOP_NOPUSH, texturePath='res:/UI/Texture/classes/toggleSwitch/compactBackground.png', height=4, leftEdgeSize=3, rightEdgeSize=3, color=eveThemeColor.THEME_FOCUS) 288 self.offFrame = StretchSpriteHorizontal(name='offFrame', parent=cont, align=uiconst.TOTOP_NOPUSH, texturePath='res:/UI/Texture/classes/toggleSwitch/compactBackground.png', height=4, leftEdgeSize=3, rightEdgeSize=3, color=eveColor.MATTE_BLACK) 289 290 291 class SwitchController(object): 292 293 def __init__(self, setting, enabled, handleSize): 294 self._setting = setting 295 self._enabled = enabled 296 self._handleSize = handleSize 297 self._hovered = False 298 self._checked = False 299 self._busy = False 300 self.on_set_checked = Signal() 301 self.on_busy_changed = Signal() 302 303 def UpdateSettings(self): 304 if self._setting: 305 if self._checked: 306 self._setting.enable() 307 else: 308 self._setting.disable() 309 310 def _GetSettingValue(self): 311 return self._setting.is_enabled() 312 313 def GetIsCheckedSettingValue(self): 314 return self._GetSettingValue() 315 316 def HasSettings(self): 317 return self._setting is not None 318 319 @property 320 def handleSize(self): 321 return self._handleSize 322 323 @handleSize.setter 324 def handleSize(self, value): 325 self._handleSize = value 326 327 @property 328 def hovered(self): 329 return self._hovered 330 331 @hovered.setter 332 def hovered(self, value): 333 self._hovered = value 334 335 @property 336 def checked(self): 337 return self._checked 338 339 @checked.setter 340 def checked(self, value): 341 if self._checked != value: 342 self.SetChecked(value) 343 344 def ForceSetCheckedValue(self, value): 345 self._checked = value 346 347 def SetChecked(self, value): 348 self.on_set_checked(value) 349 350 @property 351 def disabled(self): 352 return not self._enabled 353 354 @disabled.setter 355 def disabled(self, value): 356 self._enabled = not value 357 358 @property 359 def busy(self): 360 return self._busy 361 362 @busy.setter 363 def busy(self, value): 364 if value != self._busy: 365 self._busy = value 366 self.on_busy_changed() 367 368 369 BUSY_ANIMATION_DURATION = 2.0 370 ARROWS_TEXTURE = 'res:/UI/Texture/Classes/Industry/CenterBar/arrows.png' 371 ARROWS_TEXTURE_SEGMENT_COUNT = 6 372 ARROWS_TEXTURE_MASK = 'res:/UI/Texture/Classes/Industry/CenterBar/arrowMask.png' 373 374 class ArrowCont(Transform): 375 default_name = 'arrowCont' 376 default_align = carbonui.Align.TOALL 377 default_rotationCenter = (0.5, 0.5) 378 379 def ApplyAttributes(self, attributes): 380 super(ArrowCont, self).ApplyAttributes(attributes) 381 self.switchController = attributes.switchController 382 self._arrows = Sprite(name='arrows', parent=self, texturePath=ARROWS_TEXTURE_MASK, textureSecondaryPath=ARROWS_TEXTURE, spriteEffect=trinity.TR2_SFX_MODULATE, color=eveColor.LED_GREY, idx=0, align=carbonui.Align.TOALL, state=uiconst.UI_DISABLED) 383 self._arrows.tileXSecondary = True 384 x_translation = 1.0 / float(ARROWS_TEXTURE_SEGMENT_COUNT) 385 self._arrows.translationSecondary = (-x_translation, 0) 386 387 @threadutils.threaded 388 def _start_arrow_animation(self): 389 if self.destroyed: 390 return 391 self._animate_reset_arrow() 392 if self.switchController.busy: 393 animations.FadeIn(self._arrows) 394 self._loop_arrow_animation() 395 396 def _stop_arrow_animation(self): 397 if self.destroyed: 398 return 399 self._animate_reset_arrow() 400 if not self.switchController.busy: 401 self._arrows.opacity = 0.0 402 animations.StopAnimation(self._arrows, 'translationSecondary') 403 404 def _loop_arrow_animation(self): 405 if not self.switchController.busy and not self.destroyed: 406 return 407 x_translation = 1.0 / float(ARROWS_TEXTURE_SEGMENT_COUNT) 408 animations.MorphVector2(self._arrows, 'translationSecondary', (x_translation, 0.0), (-x_translation, 0.0), duration=BUSY_ANIMATION_DURATION, curveType=uiconst.ANIM_LINEAR, loops=uiconst.ANIM_REPEAT) 409 410 def _animate_reset_arrow(self): 411 x_translation = 1.0 / float(ARROWS_TEXTURE_SEGMENT_COUNT) 412 diff = math.fabs(-x_translation - self._arrows.translationSecondary[0]) 413 if diff > 0.001: 414 duration = diff / x_translation / 2.0 * BUSY_ANIMATION_DURATION 415 animations.MorphVector2(self._arrows, 'translationSecondary', self._arrows.translationSecondary, (-x_translation, 0.0), duration=duration, curveType=uiconst.ANIM_LINEAR) 416 if self.switchController.checked: 417 self.rotation = 0 418 else: 419 self.rotation = math.pi