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