The death of GLucose! Hear ye, hear ye.
[SugarCubes.git] / _UIImplementation.pde
1 /**
2 * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
3 *
4 * //\\ //\\ //\\ //\\
5 * ///\\\ ///\\\ ///\\\ ///\\\
6 * \\\/// \\\/// \\\/// \\\///
7 * \\// \\// \\// \\//
8 *
9 * EXPERTS ONLY!! EXPERTS ONLY!!
10 *
11 * Custom UI components using the framework.
12 */
13
14 class UIBlendMode extends UIWindow {
15 public UIBlendMode(float x, float y, float w, float h) {
16 super(lx.ui, "BLEND MODE", x, y, w, h);
17 List<UIScrollList.Item> items = new ArrayList<UIScrollList.Item>();
18 for (LXTransition t : transitions) {
19 items.add(new TransitionScrollItem(t));
20 }
21 final UIScrollList tList;
22 (tList = new UIScrollList(1, UIWindow.TITLE_LABEL_HEIGHT, w-2, 60)).setItems(items).addToContainer(this);
23
24 lx.engine.getDeck(RIGHT_DECK).addListener(new LXDeck.AbstractListener() {
25 public void faderTransitionDidChange(LXDeck deck, LXTransition transition) {
26 tList.redraw();
27 }
28 });
29 }
30
31 class TransitionScrollItem extends UIScrollList.AbstractItem {
32 private final LXTransition transition;
33 private final String label;
34
35 TransitionScrollItem(LXTransition transition) {
36 this.transition = transition;
37 this.label = className(transition, "Transition");
38 }
39
40 public String getLabel() {
41 return label;
42 }
43
44 public boolean isSelected() {
45 return this.transition == lx.engine.getDeck(RIGHT_DECK).getFaderTransition();
46 }
47
48 public boolean isPending() {
49 return false;
50 }
51
52 public void onMousePressed() {
53 lx.engine.getDeck(RIGHT_DECK).setFaderTransition(this.transition);
54 }
55 }
56
57 }
58
59 class UICrossfader extends UIWindow {
60
61 private final UIToggleSet displayMode;
62
63 public UICrossfader(float x, float y, float w, float h) {
64 super(lx.ui, "CROSSFADER", x, y, w, h);
65
66 new UISlider(4, UIWindow.TITLE_LABEL_HEIGHT, w-9, 32).setParameter(lx.engine.getDeck(RIGHT_DECK).getFader()).addToContainer(this);
67 (displayMode = new UIToggleSet(4, UIWindow.TITLE_LABEL_HEIGHT + 36, w-9, 20)).setOptions(new String[] { "A", "COMP", "B" }).setValue("COMP").addToContainer(this);
68 }
69
70 public UICrossfader setDisplayMode(String value) {
71 displayMode.setValue(value);
72 return this;
73 }
74
75 public String getDisplayMode() {
76 return displayMode.getValue();
77 }
78 }
79
80 class UIEffects extends UIWindow {
81 UIEffects(float x, float y, float w, float h) {
82 super(lx.ui, "FX", x, y, w, h);
83
84 int yp = UIWindow.TITLE_LABEL_HEIGHT;
85 List<UIScrollList.Item> items = new ArrayList<UIScrollList.Item>();
86 int i = 0;
87 for (LXEffect fx : effectsArr) {
88 items.add(new FXScrollItem(fx, i++));
89 }
90 final UIScrollList effectsList = new UIScrollList(1, yp, w-2, 60).setItems(items);
91 effectsList.addToContainer(this);
92 yp += effectsList.getHeight() + 10;
93
94 final UIKnob[] parameterKnobs = new UIKnob[4];
95 for (int ki = 0; ki < parameterKnobs.length; ++ki) {
96 parameterKnobs[ki] = new UIKnob(5 + 34*(ki % 4), yp + (ki/4) * 48);
97 parameterKnobs[ki].addToContainer(this);
98 }
99
100 LXParameterListener fxListener = new LXParameterListener() {
101 public void onParameterChanged(LXParameter parameter) {
102 int i = 0;
103 for (LXParameter p : getSelectedEffect().getParameters()) {
104 if (i >= parameterKnobs.length) {
105 break;
106 }
107 if (p instanceof BasicParameter) {
108 parameterKnobs[i++].setParameter((BasicParameter) p);
109 }
110 }
111 while (i < parameterKnobs.length) {
112 parameterKnobs[i++].setParameter(null);
113 }
114 }
115 };
116
117 selectedEffect.addListener(fxListener);
118 fxListener.onParameterChanged(null);
119
120 }
121
122 class FXScrollItem extends UIScrollList.AbstractItem {
123
124 private final LXEffect effect;
125 private final int index;
126 private final String label;
127
128 FXScrollItem(LXEffect effect, int index) {
129 this.effect = effect;
130 this.index = index;
131 this.label = className(effect, "Effect");
132 }
133
134 public String getLabel() {
135 return label;
136 }
137
138 public boolean isSelected() {
139 return !effect.isEnabled() && (effect == getSelectedEffect());
140 }
141
142 public boolean isPending() {
143 return effect.isEnabled();
144 }
145
146 public void onMousePressed() {
147 if (effect == getSelectedEffect()) {
148 if (effect.isMomentary()) {
149 effect.enable();
150 } else {
151 effect.toggle();
152 }
153 } else {
154 selectedEffect.setValue(index);
155 }
156 }
157
158 public void onMouseReleased() {
159 if (effect.isMomentary()) {
160 effect.disable();
161 }
162 }
163
164 }
165
166 }
167
168 class UIOutput extends UIWindow {
169 public UIOutput(GrizzlyOutput[] grizzlies, float x, float y, float w, float h) {
170 super(lx.ui, "OUTPUT", x, y, w, h);
171 float yp = UIWindow.TITLE_LABEL_HEIGHT;
172
173 final UIScrollList outputs = new UIScrollList(1, UIWindow.TITLE_LABEL_HEIGHT, w-2, 80);
174
175 List<UIScrollList.Item> items = new ArrayList<UIScrollList.Item>();
176 for (GrizzlyOutput grizzly : grizzlies) {
177 items.add(new GrizzlyScrollItem(grizzly));
178 grizzly.enabled.addListener(new LXParameterListener() {
179 public void onParameterChanged(LXParameter parameter) {
180 outputs.redraw();
181 }
182 });
183 }
184 outputs.setItems(items).addToContainer(this);
185 }
186
187 class GrizzlyScrollItem extends UIScrollList.AbstractItem {
188 final GrizzlyOutput output;
189
190 GrizzlyScrollItem(GrizzlyOutput output) {
191 this.output = output;
192 }
193
194 public String getLabel() {
195 return output.ipAddress;
196 }
197
198 public boolean isSelected() {
199 return output.enabled.isOn();
200 }
201
202 public void onMousePressed() {
203 output.enabled.setValue(!isSelected());
204 }
205 }
206 }
207
208 class UITempo extends UIWindow {
209
210 private final UIButton tempoButton;
211
212 UITempo(float x, float y, float w, float h) {
213 super(lx.ui, "TEMPO", x, y, w, h);
214 tempoButton = new UIButton(4, UIWindow.TITLE_LABEL_HEIGHT, w-10, 20) {
215 protected void onToggle(boolean active) {
216 if (active) {
217 lx.tempo.tap();
218 }
219 }
220 }.setMomentary(true);
221 tempoButton.addToContainer(this);
222 new UITempoBlipper(8, UIWindow.TITLE_LABEL_HEIGHT + 5, 12, 12).addToContainer(this);
223 }
224
225 class UITempoBlipper extends UIObject {
226 UITempoBlipper(float x, float y, float w, float h) {
227 super(x, y, w, h);
228 }
229
230 void onDraw(UI ui, PGraphics pg) {
231 tempoButton.setLabel("" + ((int)(lx.tempo.bpm() * 10)) / 10.);
232
233 // Overlay tempo thing with openGL, redraw faster than button UI
234 pg.fill(color(0, 0, 24 - 8*lx.tempo.rampf()));
235 pg.noStroke();
236 pg.rect(0, 0, width, height);
237
238 redraw();
239 }
240 }
241
242 }
243
244 class UIMapping extends UIWindow {
245
246 private static final String MAP_MODE_ALL = "ALL";
247 private static final String MAP_MODE_CHANNEL = "CHNL";
248 private static final String MAP_MODE_CUBE = "CUBE";
249
250 private static final String CUBE_MODE_ALL = "ALL";
251 private static final String CUBE_MODE_STRIP = "SNGL";
252 private static final String CUBE_MODE_PATTERN = "PTRN";
253
254 private final MappingTool mappingTool;
255
256 private final UIIntegerBox channelBox;
257 private final UIIntegerBox cubeBox;
258 private final UIIntegerBox stripBox;
259
260 UIMapping(MappingTool tool, float x, float y, float w, float h) {
261 super(lx.ui, "MAPPING", x, y, w, h);
262 mappingTool = tool;
263
264 int yp = UIWindow.TITLE_LABEL_HEIGHT;
265 new UIToggleSet(4, yp, w-10, 20) {
266 protected void onToggle(String value) {
267 if (value == MAP_MODE_ALL) mappingTool.mappingMode = mappingTool.MAPPING_MODE_ALL;
268 else if (value == MAP_MODE_CHANNEL) mappingTool.mappingMode = mappingTool.MAPPING_MODE_CHANNEL;
269 else if (value == MAP_MODE_CUBE) mappingTool.mappingMode = mappingTool.MAPPING_MODE_SINGLE_CUBE;
270 }
271 }.setOptions(new String[] { MAP_MODE_ALL, MAP_MODE_CHANNEL, MAP_MODE_CUBE }).addToContainer(this);
272 yp += 24;
273 new UILabel(4, yp+8, w-10, 20).setLabel("CHANNEL ID").addToContainer(this);
274 yp += 24;
275 (channelBox = new UIIntegerBox(4, yp, w-10, 20) {
276 protected void onValueChange(int value) {
277 mappingTool.setChannel(value-1);
278 }
279 }).setRange(1, mappingTool.numChannels()).addToContainer(this);
280 yp += 24;
281
282 new UILabel(4, yp+8, w-10, 20).setLabel("CUBE ID").addToContainer(this);
283 yp += 24;
284 (cubeBox = new UIIntegerBox(4, yp, w-10, 20) {
285 protected void onValueChange(int value) {
286 mappingTool.setCube(value-1);
287 }
288 }).setRange(1, model.cubes.size()).addToContainer(this);
289 yp += 24;
290
291 yp += 10;
292
293 new UIScrollList(1, yp, w-2, 60).setItems(Arrays.asList(new UIScrollList.Item[] {
294 new ColorScrollItem(ColorScrollItem.COLOR_RED),
295 new ColorScrollItem(ColorScrollItem.COLOR_GREEN),
296 new ColorScrollItem(ColorScrollItem.COLOR_BLUE),
297 })).addToContainer(this);
298 yp += 64;
299
300 new UILabel(4, yp+8, w-10, 20).setLabel("STRIP MODE").addToContainer(this);
301 yp += 24;
302
303 new UIToggleSet(4, yp, w-10, 20) {
304 protected void onToggle(String value) {
305 if (value == CUBE_MODE_ALL) mappingTool.cubeMode = mappingTool.CUBE_MODE_ALL;
306 else if (value == CUBE_MODE_STRIP) mappingTool.cubeMode = mappingTool.CUBE_MODE_SINGLE_STRIP;
307 else if (value == CUBE_MODE_PATTERN) mappingTool.cubeMode = mappingTool.CUBE_MODE_STRIP_PATTERN;
308 }
309 }.setOptions(new String[] { CUBE_MODE_ALL, CUBE_MODE_STRIP, CUBE_MODE_PATTERN }).addToContainer(this);
310
311 yp += 24;
312 new UILabel(4, yp+8, w-10, 20).setLabel("STRIP ID").addToContainer(this);
313
314 yp += 24;
315 (stripBox = new UIIntegerBox(4, yp, w-10, 20) {
316 protected void onValueChange(int value) {
317 mappingTool.setStrip(value-1);
318 }
319 }).setRange(1, Cube.STRIPS_PER_CUBE).addToContainer(this);
320
321 }
322
323 public void setChannelID(int value) {
324 channelBox.setValue(value);
325 }
326
327 public void setCubeID(int value) {
328 cubeBox.setValue(value);
329 }
330
331 public void setStripID(int value) {
332 stripBox.setValue(value);
333 }
334
335 class ColorScrollItem extends UIScrollList.AbstractItem {
336
337 public static final int COLOR_RED = 1;
338 public static final int COLOR_GREEN = 2;
339 public static final int COLOR_BLUE = 3;
340
341 private final int colorChannel;
342
343 ColorScrollItem(int colorChannel) {
344 this.colorChannel = colorChannel;
345 }
346
347 public String getLabel() {
348 switch (colorChannel) {
349 case COLOR_RED: return "Red";
350 case COLOR_GREEN: return "Green";
351 case COLOR_BLUE: return "Blue";
352 }
353 return "";
354 }
355
356 public boolean isSelected() {
357 switch (colorChannel) {
358 case COLOR_RED: return mappingTool.channelModeRed;
359 case COLOR_GREEN: return mappingTool.channelModeGreen;
360 case COLOR_BLUE: return mappingTool.channelModeBlue;
361 }
362 return false;
363 }
364
365 public void onMousePressed() {
366 switch (colorChannel) {
367 case COLOR_RED: mappingTool.channelModeRed = !mappingTool.channelModeRed; break;
368 case COLOR_GREEN: mappingTool.channelModeGreen = !mappingTool.channelModeGreen; break;
369 case COLOR_BLUE: mappingTool.channelModeBlue = !mappingTool.channelModeBlue; break;
370 }
371 }
372 }
373 }
374
375 class UIDebugText extends UIContext {
376
377 private String line1 = "";
378 private String line2 = "";
379
380 UIDebugText(float x, float y, float w, float h) {
381 super(lx.ui, x, y, w, h);
382 }
383
384 public UIDebugText setText(String line1) {
385 return setText(line1, "");
386 }
387
388 public UIDebugText setText(String line1, String line2) {
389 if (!line1.equals(this.line1) || !line2.equals(this.line2)) {
390 this.line1 = line1;
391 this.line2 = line2;
392 setVisible(line1.length() + line2.length() > 0);
393 redraw();
394 }
395 return this;
396 }
397
398 protected void onDraw(UI ui, PGraphics pg) {
399 super.onDraw(ui, pg);
400 if (line1.length() + line2.length() > 0) {
401 pg.noStroke();
402 pg.fill(#444444);
403 pg.rect(0, 0, width, height);
404 pg.textFont(ui.getItemFont());
405 pg.textSize(10);
406 pg.textAlign(LEFT, TOP);
407 pg.fill(#cccccc);
408 pg.text(line1, 4, 4);
409 pg.text(line2, 4, 24);
410 }
411 }
412 }
413
414 class UISpeed extends UIWindow {
415
416 final BasicParameter speed;
417
418 UISpeed(float x, float y, float w, float h) {
419 super(lx.ui, "SPEED", x, y, w, h);
420 speed = new BasicParameter("SPEED", 0.5);
421 speed.addListener(new LXParameterListener() {
422 public void onParameterChanged(LXParameter parameter) {
423 lx.setSpeed(parameter.getValuef() * 2);
424 }
425 });
426 new UISlider(4, UIWindow.TITLE_LABEL_HEIGHT, w-10, 20).setParameter(speed).addToContainer(this);
427 }
428 }
429
430 class UIMidi extends UIWindow {
431
432 private final UIToggleSet deckMode;
433 private final UIButton logMode;
434
435 UIMidi(final MidiEngine midiEngine, float x, float y, float w, float h) {
436 super(lx.ui, "MIDI", x, y, w, h);
437
438 // Processing compiler doesn't seem to get that list of class objects also conform to interface
439 List<UIScrollList.Item> scrollItems = new ArrayList<UIScrollList.Item>();
440 for (SCMidiInput mc : midiEngine.getControllers()) {
441 scrollItems.add(mc);
442 }
443 final UIScrollList scrollList;
444 (scrollList = new UIScrollList(1, UIWindow.TITLE_LABEL_HEIGHT, w-2, 100)).setItems(scrollItems).addToContainer(this);
445 (deckMode = new UIToggleSet(4, 130, 90, 20) {
446 protected void onToggle(String value) {
447 midiEngine.setFocusedDeck(value == "A" ? 0 : 1);
448 }
449 }).setOptions(new String[] { "A", "B" }).addToContainer(this);
450 (logMode = new UIButton(98, 130, w-103, 20)).setLabel("LOG").addToContainer(this);
451
452 SCMidiInputListener listener = new SCMidiInputListener() {
453 public void onEnabled(SCMidiInput controller, boolean enabled) {
454 scrollList.redraw();
455 }
456 };
457 for (SCMidiInput mc : midiEngine.getControllers()) {
458 mc.addListener(listener);
459 }
460
461 midiEngine.addListener(new MidiEngineListener() {
462 public void onFocusedDeck(int deckIndex) {
463 deckMode.setValue(deckIndex == 0 ? "A" : "B");
464 }
465 });
466
467 }
468
469 public boolean logMidi() {
470 return logMode.isActive();
471 }
472
473 public LXDeck getFocusedDeck() {
474 return lx.engine.getDeck(deckMode.getValue() == "A" ? LEFT_DECK : RIGHT_DECK);
475 }
476 }
477
478 String className(Object p, String suffix) {
479 String s = p.getClass().getName();
480 int li;
481 if ((li = s.lastIndexOf(".")) > 0) {
482 s = s.substring(li + 1);
483 }
484 if (s.indexOf("SugarCubes$") == 0) {
485 s = s.substring("SugarCubes$".length());
486 }
487 if ((suffix != null) && ((li = s.indexOf(suffix)) != -1)) {
488 s = s.substring(0, li);
489 }
490 return s;
491 }