1 /**
2 Film-strip on/off switch.
3 
4 Copyright: Ethan Reker 2017.
5 Copyright: Guillaume Piolat 2018.
6 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 */
8 
9 module dplug.flatwidgets.flatswitch;
10 
11 import std.math;
12 import dplug.core.math;
13 import dplug.gui.element;
14 import dplug.client.params;
15 
16 class UIImageSwitch : UIElement, IParameterListener
17 {
18 public:
19 nothrow:
20 @nogc:
21 
22     enum Orientation
23     {
24         vertical,
25         horizontal
26     }
27 
28     @ScriptProperty
29     {
30         Orientation orientation = Orientation.vertical;
31     }
32 
33     this(UIContext context, BoolParameter param, OwnedImage!RGBA onImage, OwnedImage!RGBA offImage)
34     {
35         super(context, flagRaw);
36         _param = param;
37         _param.addListener(this);
38 
39         _onImage = onImage;
40         _offImage = offImage;
41         assert(_onImage.h == _offImage.h);
42         assert(_onImage.w == _offImage.w);
43         _width = _onImage.w;
44         _height = _onImage.h;
45 
46         _onImageScaled = mallocNew!(OwnedImage!RGBA)();
47         _offImageScaled = mallocNew!(OwnedImage!RGBA)();
48 
49     }
50 
51     ~this()
52     {
53         _param.removeListener(this);
54         _onImageScaled.destroyFree();
55         _offImageScaled.destroyFree();
56     }
57 
58     bool getState()
59     {
60       return unsafeObjectCast!BoolParameter(_param).valueAtomic();
61     }
62 
63     override void reflow()
64     {
65         _onImageScaled.size(position.width, position.height);
66         _offImageScaled.size(position.width, position.height);
67         context.globalImageResizer.resizeImage_sRGBWithAlpha(_onImage.toRef, _onImageScaled.toRef);
68         context.globalImageResizer.resizeImage_sRGBWithAlpha(_offImage.toRef, _offImageScaled.toRef);
69     }
70 
71     override void onDrawRaw(ImageRef!RGBA rawMap, box2i[] dirtyRects)
72     {
73           auto _currentImage = getState() ? _onImageScaled : _offImageScaled;
74           foreach(dirtyRect; dirtyRects)
75           {
76               auto croppedRawIn = _currentImage.toRef.cropImageRef(dirtyRect);
77               auto croppedRawOut = rawMap.cropImageRef(dirtyRect);
78 
79               int w = dirtyRect.width;
80               int h = dirtyRect.height;
81 
82               for(int j = 0; j < h; ++j)
83               {
84                   RGBA[] input = croppedRawIn.scanline(j);
85                   RGBA[] output = croppedRawOut.scanline(j);
86 
87                   for(int i = 0; i < w; ++i)
88                   {
89                       ubyte alpha = input[i].a;
90 
91                       RGBA color = blendColor(input[i], output[i], alpha);
92                       output[i] = color;
93                   }
94               }
95           }
96     }
97 
98     override Click onMouseClick(int x, int y, int button, bool isDoubleClick, MouseState mstate)
99     {
100         if (!_canBeDragged)
101         {
102             // inside gesture, refuse new clicks that could call
103             // excess beginParamEdit()/endParamEdit()
104             return Click.unhandled;
105         }
106 
107         if (mstate.altPressed) // reset on ALT + click
108         {
109             _param.beginParamEdit();
110             _param.setFromGUI(_param.defaultValue());
111         }
112         else
113         {
114             // Any click => invert
115             // Note: double-click doesn't reset to default, would be annoying
116             _param.beginParamEdit();
117             _param.setFromGUI(!_param.value());
118         }
119         _canBeDragged = false;
120         return Click.startDrag; 
121     }
122 
123     override void onMouseEnter()
124     {
125         _param.beginParamHover();
126         setDirtyWhole();
127     }
128 
129     override void onMouseMove(int x, int y, int dx, int dy, MouseState mstate)
130     {        
131     }
132 
133     override void onMouseExit()
134     {
135         _param.endParamHover();
136         setDirtyWhole();
137     }
138 
139     override void onBeginDrag()
140     {
141     }
142 
143     override void onStopDrag()
144     {
145         // End parameter edit at end of dragging, even if no parameter change happen,
146         // so that touch automation restore previous parameter value at the end of the mouse 
147         // gesture.
148         _param.endParamEdit();
149         _canBeDragged = true;
150     }
151     
152     override void onMouseDrag(int x, int y, int dx, int dy, MouseState mstate)
153     {        
154     }
155 
156     override void onParameterChanged(Parameter sender) nothrow @nogc
157     {
158         setDirtyWhole();
159     }
160 
161     override void onBeginParameterEdit(Parameter sender)
162     {
163     }
164 
165     override void onEndParameterEdit(Parameter sender)
166     {
167     }
168 
169     override void onBeginParameterHover(Parameter sender)
170     {
171     }
172 
173     override void onEndParameterHover(Parameter sender)
174     {
175     }
176 
177 protected:
178 
179     BoolParameter _param;
180     bool _state;
181     OwnedImage!RGBA _onImage;
182     OwnedImage!RGBA _offImage;
183     OwnedImage!RGBA _onImageScaled;
184     OwnedImage!RGBA _offImageScaled;
185     int _width;
186     int _height;
187 
188     /// To prevent multiple-clicks having an adverse effect on automation.
189     bool _canBeDragged = true;
190 }