1 /** 2 On/Off switch. 3 4 Copyright: Copyright Auburn Sounds 2015-2017. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 Authors: Guillaume Piolat 7 */ 8 module dplug.pbrwidgets.onoffswitch; 9 10 import std.math; 11 import dplug.core.math; 12 import dplug.gui.element; 13 import dplug.gui.bufferedelement; 14 import dplug.client.params; 15 16 class UIOnOffSwitch : UIElement, IParameterListener 17 { 18 public: 19 nothrow: 20 @nogc: 21 22 enum Orientation 23 { 24 vertical, 25 horizontal 26 } 27 @ScriptProperty RGBA diffuseOff = RGBA(230, 80, 43, 0); 28 @ScriptProperty RGBA diffuseOn = RGBA(230, 80, 43, 200); 29 @ScriptProperty RGBA material = RGBA(192, 10, 128, 255); 30 @ScriptProperty float animationTimeConstant = 10.0f; 31 @ScriptProperty ushort depthLow = 0; 32 @ScriptProperty ushort depthHigh = 30000; 33 @ScriptProperty ushort holeDepth = 0; 34 @ScriptProperty Orientation orientation = Orientation.vertical; 35 36 this(UIContext context, BoolParameter param) 37 { 38 super(context, flagAnimated | flagPBR); 39 _param = param; 40 _param.addListener(this); 41 _animation = 0.0f; 42 } 43 44 ~this() 45 { 46 _param.removeListener(this); 47 } 48 49 override void onAnimate(double dt, double time) nothrow @nogc 50 { 51 float target = _param.valueAtomic() ? 1 : 0; 52 53 float newAnimation = lerp(_animation, target, 1.0 - exp(-dt * animationTimeConstant)); 54 55 if (abs(newAnimation - _animation) > 0.001f) 56 { 57 _animation = newAnimation; 58 setDirtyWhole(); 59 } 60 } 61 62 override void onDrawPBR(ImageRef!RGBA diffuseMap, ImageRef!L16 depthMap, ImageRef!RGBA materialMap, box2i[] dirtyRects) nothrow @nogc 63 { 64 // The switch is in a subrect 65 int width = _position.width; 66 int height = _position.height; 67 float border = 0.1f; 68 box2i switchRect = box2i( cast(int)(0.5f + width * border), 69 cast(int)(0.5f + height * border), 70 cast(int)(0.5f + width * (1 - border)), 71 cast(int)(0.5f + height * (1 - border)) ); 72 73 ubyte red = cast(ubyte)(lerp!float(diffuseOff.r, diffuseOn.r, _animation)); 74 ubyte green = cast(ubyte)(lerp!float(diffuseOff.g, diffuseOn.g, _animation)); 75 ubyte blue = cast(ubyte)(lerp!float(diffuseOff.b, diffuseOn.b, _animation)); 76 int emissive = cast(ubyte)(lerp!float(diffuseOff.a, diffuseOn.a, _animation)); 77 78 if (isMouseOver || isDragged) 79 emissive += 40; 80 81 if (emissive > 255) 82 emissive = 255; 83 84 // Workaround issue https://issues.dlang.org/show_bug.cgi?id=23076 85 // Regular should not be inlined here. 86 static float lerpfloat(float a, float b, float t) pure nothrow @nogc 87 { 88 pragma(inline, false); 89 return t * b + (1 - t) * a; 90 } 91 92 L16 depthA = L16(cast(short)(lerpfloat(depthHigh, depthLow, _animation))); 93 L16 depthB = L16(cast(short)(lerpfloat(depthLow, depthHigh, _animation))); 94 95 RGBA diffuseColor = RGBA(red, green, blue, cast(ubyte)emissive); 96 97 foreach(dirtyRect; dirtyRects) 98 { 99 auto cDepth = depthMap.cropImageRef(dirtyRect); 100 101 // Write a plain color in the diffuse and material map. 102 box2i validRect = dirtyRect.intersection(switchRect); 103 if (!validRect.empty) 104 { 105 diffuseMap.cropImageRef(validRect).fillAll(diffuseColor); 106 materialMap.cropImageRef(validRect).fillAll(material); 107 } 108 109 // dig a hole 110 cDepth.fillAll(L16(holeDepth)); 111 112 if (orientation == Orientation.vertical) 113 verticalSlope(cDepth, switchRect, depthA, depthB); 114 else 115 horizontalSlope(cDepth, switchRect, depthA, depthB); 116 } 117 } 118 119 override Click onMouseClick(int x, int y, int button, bool isDoubleClick, MouseState mstate) 120 { 121 // ALT + click => set it to default 122 if (mstate.altPressed) // reset on ALT + click 123 { 124 _param.beginParamEdit(); 125 _param.setFromGUI(_param.defaultValue()); 126 _param.endParamEdit(); 127 } 128 else 129 { 130 // Any click => invert 131 // Note: double-click doesn't reset to default, would be annoying 132 _param.beginParamEdit(); 133 _param.setFromGUI(!_param.value()); 134 _param.endParamEdit(); 135 } 136 return Click.startDrag; 137 } 138 139 override void onMouseEnter() 140 { 141 _param.beginParamHover(); 142 setDirtyWhole(); 143 } 144 145 override void onMouseExit() 146 { 147 _param.endParamHover(); 148 setDirtyWhole(); 149 } 150 151 override void onParameterChanged(Parameter sender) nothrow @nogc 152 { 153 setDirtyWhole(); 154 } 155 156 override void onBeginParameterEdit(Parameter sender) 157 { 158 } 159 160 override void onEndParameterEdit(Parameter sender) 161 { 162 } 163 164 override void onBeginParameterHover(Parameter sender) 165 { 166 } 167 168 override void onEndParameterHover(Parameter sender) 169 { 170 } 171 172 protected: 173 174 /// The parameter this switch is linked with. 175 BoolParameter _param; 176 177 private: 178 float _animation; 179 }