1 /*
2 * DISTRHO Plugin Framework (DPF)
3 * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any purpose with
6 * or without fee is hereby granted, provided that the above copyright notice and this
7 * permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "DistrhoUI.hpp"
18
19 START_NAMESPACE_DISTRHO
20
21 /**
22 We need the Color class from DGL.
23 */
24 using DGL_NAMESPACE::Color;
25
26 /**
27 Smooth meters a bit.
28 */
29 static const float kSmoothMultiplier = 3.0f;
30
31 // -----------------------------------------------------------------------------------------------------------
32
33 class ExampleUIMeters : public UI
34 {
35 public:
ExampleUIMeters()36 ExampleUIMeters()
37 : UI(128, 512),
38 // default color is green
39 fColor(93, 231, 61),
40 // which is value 0
41 fColorValue(0),
42 // init meter values to 0
43 fOutLeft(0.0f),
44 fOutRight(0.0f)
45 {
46 setGeometryConstraints(32, 128, false);
47 }
48
49 protected:
50 /* --------------------------------------------------------------------------------------------------------
51 * DSP/Plugin Callbacks */
52
53 /**
54 A parameter has changed on the plugin side.
55 This is called by the host to inform the UI about parameter changes.
56 */
parameterChanged(uint32_t index,float value)57 void parameterChanged(uint32_t index, float value) override
58 {
59 switch (index)
60 {
61 case 0: // color
62 updateColor(std::round(value));
63 break;
64
65 case 1: // out-left
66 value = (fOutLeft * kSmoothMultiplier + value) / (kSmoothMultiplier + 1.0f);
67
68 /**/ if (value < 0.001f) value = 0.0f;
69 else if (value > 0.999f) value = 1.0f;
70
71 if (fOutLeft != value)
72 {
73 fOutLeft = value;
74 repaint();
75 }
76 break;
77
78 case 2: // out-right
79 value = (fOutRight * kSmoothMultiplier + value) / (kSmoothMultiplier + 1.0f);
80
81 /**/ if (value < 0.001f) value = 0.0f;
82 else if (value > 0.999f) value = 1.0f;
83
84 if (fOutRight != value)
85 {
86 fOutRight = value;
87 repaint();
88 }
89 break;
90 }
91 }
92
93 /**
94 A state has changed on the plugin side.
95 This is called by the host to inform the UI about state changes.
96 */
stateChanged(const char *,const char *)97 void stateChanged(const char*, const char*) override
98 {
99 // nothing here
100 }
101
102 /* --------------------------------------------------------------------------------------------------------
103 * Widget Callbacks */
104
105 /**
106 The NanoVG drawing function.
107 */
onNanoDisplay()108 void onNanoDisplay() override
109 {
110 static const Color kColorBlack(0, 0, 0);
111 static const Color kColorRed(255, 0, 0);
112 static const Color kColorYellow(255, 255, 0);
113
114 // get meter values
115 const float outLeft(fOutLeft);
116 const float outRight(fOutRight);
117
118 // tell DSP side to reset meter values
119 setState("reset", "");
120
121 // useful vars
122 const float halfWidth = static_cast<float>(getWidth())/2;
123 const float redYellowHeight = static_cast<float>(getHeight())*0.2f;
124 const float yellowBaseHeight = static_cast<float>(getHeight())*0.4f;
125 const float baseBaseHeight = static_cast<float>(getHeight())*0.6f;
126
127 // create gradients
128 Paint fGradient1 = linearGradient(0.0f, 0.0f, 0.0f, redYellowHeight, kColorRed, kColorYellow);
129 Paint fGradient2 = linearGradient(0.0f, redYellowHeight, 0.0f, yellowBaseHeight, kColorYellow, fColor);
130
131 // paint left meter
132 beginPath();
133 rect(0.0f, 0.0f, halfWidth-1.0f, redYellowHeight);
134 fillPaint(fGradient1);
135 fill();
136 closePath();
137
138 beginPath();
139 rect(0.0f, redYellowHeight-0.5f, halfWidth-1.0f, yellowBaseHeight);
140 fillPaint(fGradient2);
141 fill();
142 closePath();
143
144 beginPath();
145 rect(0.0f, redYellowHeight+yellowBaseHeight-1.5f, halfWidth-1.0f, baseBaseHeight);
146 fillColor(fColor);
147 fill();
148 closePath();
149
150 // paint left black matching output level
151 beginPath();
152 rect(0.0f, 0.0f, halfWidth-1.0f, (1.0f-outLeft)*getHeight());
153 fillColor(kColorBlack);
154 fill();
155 closePath();
156
157 // paint right meter
158 beginPath();
159 rect(halfWidth+1.0f, 0.0f, halfWidth-2.0f, redYellowHeight);
160 fillPaint(fGradient1);
161 fill();
162 closePath();
163
164 beginPath();
165 rect(halfWidth+1.0f, redYellowHeight-0.5f, halfWidth-2.0f, yellowBaseHeight);
166 fillPaint(fGradient2);
167 fill();
168 closePath();
169
170 beginPath();
171 rect(halfWidth+1.0f, redYellowHeight+yellowBaseHeight-1.5f, halfWidth-2.0f, baseBaseHeight);
172 fillColor(fColor);
173 fill();
174 closePath();
175
176 // paint right black matching output level
177 beginPath();
178 rect(halfWidth+1.0f, 0.0f, halfWidth-2.0f, (1.0f-outRight)*getHeight());
179 fillColor(kColorBlack);
180 fill();
181 closePath();
182 }
183
184 /**
185 Mouse press event.
186 This UI will change color when clicked.
187 */
onMouse(const MouseEvent & ev)188 bool onMouse(const MouseEvent& ev) override
189 {
190 // Test for left-clicked + pressed first.
191 if (ev.button != 1 || ! ev.press)
192 return false;
193
194 const int newColor(fColorValue == 0 ? 1 : 0);
195 updateColor(newColor);
196 setParameterValue(0, newColor);
197
198 return true;
199 }
200
201 // -------------------------------------------------------------------------------------------------------
202
203 private:
204 /**
205 Color and its matching parameter value.
206 */
207 Color fColor;
208 int fColorValue;
209
210 /**
211 Meter values.
212 These are the parameter outputs from the DSP side.
213 */
214 float fOutLeft, fOutRight;
215
216 /**
217 Update color if needed.
218 */
updateColor(const int color)219 void updateColor(const int color)
220 {
221 if (fColorValue == color)
222 return;
223
224 fColorValue = color;
225
226 switch (color)
227 {
228 case METER_COLOR_GREEN:
229 fColor = Color(93, 231, 61);
230 break;
231 case METER_COLOR_BLUE:
232 fColor = Color(82, 238, 248);
233 break;
234 }
235
236 repaint();
237 }
238
239 /**
240 Set our UI class as non-copyable and add a leak detector just in case.
241 */
242 DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExampleUIMeters)
243 };
244
245 /* ------------------------------------------------------------------------------------------------------------
246 * UI entry point, called by DPF to create a new UI instance. */
247
createUI()248 UI* createUI()
249 {
250 return new ExampleUIMeters();
251 }
252
253 // -----------------------------------------------------------------------------------------------------------
254
255 END_NAMESPACE_DISTRHO
256